サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

Create React App + React Router入門 04: フックuseRouteMatch()とuseHistory()を使う


React Router v5.1からフックAPIが備わりました(「React Router v5.1」参照)。今回は、その中からふたつのフックをご紹介します。前回の「Create React App + React Router入門 03: ルーティングを入れ子にする」でアプリケーションモジュールsrc/App.jsに書いたコード001に手を加えてゆきましょう。

01 useRouteMatch()でルートの情報を得る

ひとつ目は、パスと合致したルートの情報が収められたmatchオブジェクトを参照するuseRouteMatch()です。プロパティのうち、つぎのようにurlpathを用いました。今回はどちらも同じ値('/settings2')です。ただ、urlは遷移(<Link>)に、pathはルート(<Route>)に対応します(「match」参照)。たとえば、URLパラメータを用いたとき、ふたつの値に違いが生じます(「ささっと学ぶReact Router v4」の「matchとURLパラメーター」参照)。

src/App.js

import {

	useRouteMatch
} from 'react-router-dom';

const Settings2 = (props) => {

	const match = useRouteMatch();
	return (
		<>

			<ul>
				{settings.map((setting, id) => (
					<li key={id}>
						{/* <Link to={'/settings2/' + setting.path}>{setting.name}</Link> */}
						<Link to={match.url + '/' + setting.path}>{setting.name}</Link>
					</li>
				))}
			</ul>
			<Switch>
				{settings.map((setting, id) => (
					// <Route path={'/settings2/' + setting.path} key={id}>
					<Route path={match.path + '/' + setting.path} key={id}>
						<Details name={setting.name} path={setting.path} setValue={setting.setValue} />
					</Route>
				))}
				{/* <Route path={'/settings2/'}> */}
				<Route path={match.path + '/'}>
					<h3>Pleasse select a setting.</h3>
				</Route>
			</Switch>

		</>
	);
};

02 useHistory()でルートを遷移する

ふたつの設定コンポーネント(SettingsSettings2)の間は、リンク(<Link>)で遷移していました。これをボタン(<button>)に差し替えてみましょう(図001)。

図001■ふたつの設定のコンポーネント間をボタンで遷移する

図001左
←→
図001右

すると、遷移はボタンのonClickハンドラで行うことになります。標準のJavaScriptコードで扱うなら、window.locationを使う場面です。つぎのように、hrefプロパティにパスを与えれば、画面が遷移します。けれど今回の場合は、これは適切ではありません。「Create React App + React Router入門 02: ページの遷移と状態の保持」03「コンポーネントの遷移と状態」で<a>要素を用いて遷移したときと同じく、ページ全体がリロードしてしまうからです。つまり、状態が初期化されてしまいます。

src/App.js

const Settings = (props) => (
	<>

		{/* <p><Link to="/settings2">next >></Link></p> */}
		<p><button type="button" onClick={() => window.location.href='/settings2'}>next >></button></p>
	</>
);

JavaScriptコードによるコンポーネントの遷移にも、React Routerが提供する仕組みを使わなければなりません。それがふたつ目のフックuseHistory()です。戻り値のhistoryインスタンスにパスをpush()すれば、React Routerにより状態が保たれたまま遷移ができるのです。

src/App.js

import {

	useHistory
} from 'react-router-dom';

// const Settings = (props) => (
const Settings = (props) => {
	const history = useHistory();
	return (
		<>

			{/* <p><Link to="/settings2">next >></Link></p> */}
			<p><button type="button" onClick={() => history.push('/settings2')}>next >></button></p>
		</>
	);
};
const Settings2 = (props) => {

	const history = useHistory();
	return (
		<>


			{/* <p><Link to="/settings"><< prev</Link></p> */}
			<p><button type="button" onClick={() => history.push('/settings')}><< prev</button></p>
		</>
	);
};

これでProductsコンポーネントのタイトルの色だけでなく、新たに加えたテキストも動的に書き替えられます(図002)。手直ししたアプリケーションモジュールsrc/App.jsの記述全体は、以下のコード001のとおりです。また、サンプル001をCodeSandboxに公開しました。

コード001■フックuseRouteMatch()とuseHistory()を使う

src/App.js

import React, { useState } from 'react';
import {
	BrowserRouter as Router,
	Switch,
	Route,
	Link,
	useRouteMatch,
	useHistory
} from 'react-router-dom';
import './App.css';

function App() {
	const [color, setColor] = useState('black');
	const [color2, setColor2] = useState('black');
	const [text2, setText2] = useState('sample text');
	return (
		<Router>
			<nav>
				<ul>
					<li>
						<Link to="/">Home</Link>
					</li>
					<li>
						<Link to="/products">Products</Link>
					</li>
					<li>
						<Link to="/settings">Settings</Link>
					</li>
				</ul>
			</nav>
			<Switch>
				<Route path="/products">
					<Products color2={color2} text2={text2} />
				</Route>
				<Route path="/settings">
					<Settings setColor={setColor} />
				</Route>
				<Route path="/settings2">
					<Settings2 setColor2={setColor2} setText2={setText2} />
				</Route>
				<Route path="/">
					<Home color={color} />
				</Route>
			</Switch>
		</Router>
	);
}
const Home = (props) => (
	<h2 style={{color: props.color}}>Home</h2>
);
const Products = (props) => (
	<>
		<h2 style={{color: props.color2}}>Products</h2>
		<p>
			{props.text2 ? props.text2 : null}
		</p>
	</>
);
const Settings = (props) => {
	const history = useHistory();
	return (
		<>
			<h2>Settings</h2>
			<input type="text"
				onChange={(event) => props.setColor(event.target.value)}
			/>
			<p><button type="button" onClick={() => history.push('/settings2')}>next >></button></p>
		</>
	);
};
const Settings2 = (props) => {
	const settings = [
		{name: 'Color', path: 'color', setValue: props.setColor2},
		{name: 'Text', path: 'text', setValue: props.setText2},
	];
	const match = useRouteMatch();
	const history = useHistory();
	return (
		<>
			<h2>Settings2</h2>
			<ul>
				{settings.map((setting, id) => (
					<li key={id}>
						<Link to={match.url + '/' + setting.path}>{setting.name}</Link>
					</li>
				))}
			</ul>
			<Switch>
				{settings.map((setting, id) => (
					<Route path={match.path + '/' + setting.path} key={id}>
						<Details name={setting.name} path={setting.path} setValue={setting.setValue} />
					</Route>
				))}
				<Route path={match.path + '/'}>
					<h3>Pleasse select a setting.</h3>
				</Route>
			</Switch>
			<p><button type="button" onClick={() => history.push('/settings')}><< prev</button></p>
		</>
	);
};
const Details = (props) => {
	return (
		<label>
			{props.name}   
			<input type="text"
				onChange={(event) => props.setValue(event.target.value)}
			/>
		</label>
	);
};
export default App;

サンプル001■React Router 05: Using useRouteMatch() and useHistory()

Create React App + React Router入門


作成者: 野中文雄
作成日: 2020年05月19日


Copyright © 2001-2020 Fumio Nonaka.  All rights reserved.