ソースコードはこちら; netsphere / react-by-examples · GitLab
今回は Blog もどきを作ってみる。まだ、永続化はしない。また, Redux も使っていない。
スクリーンショット
ルーティング。
パッケージ追加
package.json
ファイルに react-router-dom パッケージを追加する。
アプリケーションの雛形を作る。
$ npx create-react-app router-test
ディレクトリを降りて、npm コマンドでパッケージを追加。
$ npm install react-router-dom
npm install コマンドのオプション:
[--save-prod|--save-dev|--save-optional] [--save-exact] [--no-save]
今では --save オプションは不要。もし書いているような記事があったら、それは古いか、古いものを確認もせずコピペしている。
package.json
ファイル:
JavaScript
- {
- "name": "03_router-routing",
- "version": "0.1.0",
- "author": "Hisashi Horikawa",
- "license": "MIT",
- "private": true,
- "description": "react-router-dom パッケージがルーティングを担当.",
- "dependencies": {
- "@testing-library/jest-dom": "^4.2.4",
- "@testing-library/react": "^9.5.0",
- "@testing-library/user-event": "^7.2.1",
- "react": "^16.13.0",
- "react-dom": "^16.13.0",
- "react-router-dom": "^5.1.2",
- "react-scripts": "3.4.0"
- },
- "scripts": {
- "start": "react-scripts start",
- "build": "react-scripts build",
- "test": "react-scripts test",
- "eject": "react-scripts eject"
- },
- "eslintConfig": {
- "extends": "react-app"
- },
- "browserslist": {
- "production": [
- ">0.2%",
- "not dead",
- "not op_mini all"
- ],
- "development": [
- "last 1 chrome version",
- "last 1 firefox version",
- "last 1 safari version"
- ]
- }
- }
今回は "react-router-dom v5.1.2以上" を使っている。react-router v3 から react-router-dom v4.0 (2017年4月) で非互換な変更があり、v3時代の記事は古くて注意が必要。一応, v3.2.6 が2020年3月にリリースされており、v3系列もメンテナンスされているようだ。
さらに v5.0 (2019年3月) が出ているが, v5 は v4 と互換性がある。
Routing
src/App.js
ファイルに routing を書く。
JavaScript
- import { BrowserRouter, Route,
- Switch,
- NavLink } from 'react-router-dom';
-
- function App() {
-
-
-
- return (<BrowserRouter>
- <nav>
- Posts
- <NavLink exact to="/">Home</NavLink>
- <NavLink to="/articles/">Articles</NavLink>
- <NavLink to="/about">About</NavLink>
- </nav>
- <Switch>
- <Route path="/about" component={About} />
- <Route exact path="/articles" component={ArticleList} />
- <Route path="/articles/new" component={ArticleEdit} />
- <Route exact path="/articles/:id" component={ArticleShow} />
- <Route path="/articles/:id/edit" component={ArticleEdit} />
- <Route exact path="/" component={Home} />
- <Route path="*" render={routeProps => (`Page not found`)} />
- </Switch>
- </BrowserRouter> );
- }
ルーティングは <BrowserRouter>
で囲む。通常のテキストや HTMLタグを含めてもよい。
<Switch>
内の <Route>
は排他になる。<Switch>
で囲まなければ、マッチしたものがすべて描画される。
マッチ条件は path
で指定する。"*"
は何にでもマッチする。exact
を付けると厳密マッチで, 付けなければ部分一致になる。path 末尾の "/" の有無は、よきにしてくれる。
<Link>
と <NavLink>
は、あたかも HTML a
タグのように, ページ遷移してくれる。履歴 history への追加もよきにしてくれる。
<NavLink>
は, to
値がWebブラウザの現在アドレス (現に表示されている) の場合, activeClassName
, activeStyle
で HTML クラス, HTMLスタイルを変更できる。
入力フォーム
いくつかポイントがある。
- 値が変更されたときの取扱い
- 保存時のエラー
- リダイレクト
先にコードを示す;
JavaScript
- handleSubmit = (event) => {
- event.preventDefault();
- const title = this.state.title.trim();
- if (!title)
- return;
-
- try {
- const article = ArticleModel.create({
- id:this.state.id, title:title, body:this.state.body});
- this.setState({id: article.id,
- redirectToShow: true});
- }
- catch (err) {
- this.setState( {error:err} );
- }
- }
-
- onFieldChanged = (event) => {
- const name = event.target.name;
-
- const value = event.target.value;
- this.setState({[name]:value});
- if (name === 'title')
- this.setState( {submitEnabled: value.trim() !== ''} );
- }
-
- render() {
- if ( this.state.redirectToShow ) {
- return <Redirect to={`/articles/${this.state.id}`} />;
- }
-
- return (<div>
- {Number(this.state.id)}
- {this.state.error ? <div>Error: {this.state.error.message}</div> : ''}
- <form onSubmit={this.handleSubmit} >
- Title: <input name="title" type="text" value={this.state.title}
- onChange={this.onFieldChanged} />
- <textarea name="body" value={this.state.body}
- onChange={this.onFieldChanged} />
- <button type="submit" disabled={this.state.submitEnabled ? '' : 'disabled'}>
- {Number(this.state.id) > 0 ? 'Update' : 'Create'}</button>
- </form>
- </div> );
- }
- }
フォームを作るとき, 値に変更があるたびに state
を更新していくが、フィールドごとにメソッドを作っていては大変。
そこで, render()
で, それぞれのHTMLタグに name
属性を付けてやる。onFieldChanged
では, イベントの target.name
プロパティでどのタグか区別できる。このイベントは, DOM Level 2 Events で定義されている。
保存時のエラーは、やはり直接エラーメッセージを表示するのではなく、state
にエラー状態を設定してやり、実際の表示は render()
内で行う。
保存が成功したときのリダイレクトも同様。JSX として <Redirect>
を返すと、リダイレクトのように動作する。現在アドレスの更新もOK.