HTML5テクニカルノート
Create React App + React Router入門 04: フックuseRouteMatch()とuseHistory()を使う
- ID: FN2005004
- Technique: ECMAScript 2015
- Library: React 16.13.1 / React Router 5.1.2
React Router v5.1からフックAPIが備わりました(「React Router v5.1」参照)。今回は、その中からふたつのフックをご紹介します。前回の「Create React App + React Router入門 03: ルーティングを入れ子にする」でアプリケーションモジュールsrc/App.js
に書いたコード001に手を加えてゆきましょう。
01 useRouteMatch()でルートの情報を得る
ひとつ目は、パスと合致したルートの情報が収められたmatch
オブジェクトを参照するuseRouteMatch()
です。プロパティのうち、つぎのようにurl
とpath
を用いました。今回はどちらも同じ値('/settings2'
)です。ただ、url
は遷移(<Link>
)に、path
はルート(<Route>
)に対応します(「match」参照)。たとえば、URLパラメータを用いたとき、ふたつの値に違いが生じます(「ささっと学ぶReact Router v4」の「matchとURLパラメーター」参照)。
src/App.jsimport { 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()でルートを遷移する
ふたつの設定コンポーネント(Settings
とSettings2
)の間は、リンク(<Link>
)で遷移していました。これをボタン(<button>
)に差し替えてみましょう(図001)。
図001■ふたつの設定のコンポーネント間をボタンで遷移する
すると、遷移はボタンのonClick
ハンドラで行うことになります。標準のJavaScriptコードで扱うなら、window.location
を使う場面です。つぎのように、href
プロパティにパスを与えれば、画面が遷移します。けれど今回の場合は、これは適切ではありません。「Create React App + React Router入門 02: ページの遷移と状態の保持」03「コンポーネントの遷移と状態」で<a>
要素を用いて遷移したときと同じく、ページ全体がリロードしてしまうからです。つまり、状態が初期化されてしまいます。
src/App.jsconst 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.jsimport { 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入門
- Create React App + React Router入門 01: ルーティングを定める
- Create React App + React Router入門 02: ページの遷移と状態の保持
- Create React App + React Router入門 03: ルーティングを入れ子にする
- Create React App + React Router入門 04: フックuseRouteMatch()とuseHistory()を使う
作成者: 野中文雄
作成日: 2020年05月19日
Copyright © 2001-2020 Fumio Nonaka. All rights reserved.