こんにちは、KOUKIです。
ReactでMovie Appの開発を行なっています。
今回は、以前実装したルーティングのより発展的な使い方を見ていきましょう。
尚、Udemyの「Working with React and Go (Golang)」を参考にしているので、よかったら受講してみてください。
前回
ページのコンポーネント化とReactのStateを実装しました。
事前準備
フォルダ/ファイル
1 2 |
touch go-movies/src/components/Categories.tsx touch go-movies/src/components/OneMovie.tsx |
ルーティング処理(Advance)
個別ページへの遷移
Movie一覧から個別ページに遷移する処理を実装します。
URLとしては、以下を想定しています。
1 |
http://localhost:3000/movies/1 |
上記の「1」は、IDを示しています。
このidをURLから取得するには、以下の実装を行います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// src/App.tsx ... export default function App() { return ( <Router> .... <div className="col-md-10"> <Switch> <Route path="/movies/:id" render={(props) => <Movie {...props} />} /> </Router> ) } const Movie = (props: any) => { let { id } = props.match.params return <h2>Movie id {id}</h2> } |
Routeタグの中でrenderプロパティを定義し「props」を渡すことで、個別ページ(Movie)にprops情報を渡すことが可能です。
そして、「props.match.params」からidを取得できます。
動作確認のため、前回実装したMovie一覧のリストにLinkを貼りましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// components/Movies.tsx import { Link } from 'react-router-dom' ... const Movies = () => { ... return ( <Fragment> <h2>Choose a movie</h2> <ul> {movies.map((m) => { return ( <li key={m.id}> <Link to={`/movies/${m.id}`}>{m.title}</Link> // Linkを貼る </li> ) })} </ul> </Fragment> ) } export default Movies |
これで、個別ページへ画面遷移ができるようになりました。
カテゴリーページの作成
カテゴリーページを作成しましょう。
1 2 3 4 5 6 |
// components/Categories.tsx const Categories = (props: any) => { return <h2>Category: {props.title}</h2> } export default Categories |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
// src/App.tsx import { BrowserRouter as Router, Switch, Route, Link, useRouteMatch } from 'react-router-dom' ... import Categories from './components/Categories' export default function App() { return ( <Router> <div className="container"> ... <div className="row"> <div className="col-md-2"> <nav> <ul className="list-group"> ... <li className="list-group-item"> <Link to="/movies">Movies</Link> </li> <li className="list-group-item"> // 追加 <Link to="/by-category">Categories</Link> </li> ... </ul> </nav> </div> <div className="col-md-10"> <Switch> ... <Route exact path="/by-category"> <CategoryPage /> </Route> <Route exact path="/by-category/comedy" render={(props) => <Categories {...props} title={`Comedy`}/>} /> <Route exact path="/by-category/drama" render={(props) => <Categories {...props} title={`Drama`}/>} /> ... </Switch> </div> </div> </div> </Router> ) } ... const CategoryPage = () => { let { path, url } = useRouteMatch() console.log(path, url) return ( <div> <h2>Categories</h2> <ul> <li><Link to={`${path}/comedy`}>Comedy</Link></li> <li><Link to={`${url}/drama`}>Drama</Link></li> </ul> </div> ) } |
CategoryPageに設定した「path, url」には、同じ値が入ります。ここでは、「/by-category」が該当しますね。
これでカテゴリーページ -> 個々のカテゴリーページへ画面遷移ができるようになりました。
リファクタリング
Movieページのコンポーネント化
Movieページをコンポーネント化します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// components/OneMovie.tsx import { useEffect, useState, Fragment } from 'react' import { Movie } from '../models/movie' const OneMovie = (props: any) => { const [movie, setMovie] = useState<Movie>({ id: 0, title: "No Movie", runtime: 0, }) useEffect(() => { ( () => { setMovie({ id: props.match.id, title: "Some movie", runtime: 150, }) } )() }, []) return ( <Fragment> <h2>Movie: {movie.title} {movie.id}</h2> <table className="table table-compact table-striped"> <thead></thead> <tbody> <tr> <td><strong>Title:</strong></td> <td>{movie.title}</td> </tr> <tr> <td><strong>Run Time:</strong></td> <td>{movie.runtime} minutes</td> </tr> </tbody> </table> </Fragment> ) } export default OneMovie |
App.tsxも修正します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
// src/App.tsx ... import OneMovie from './components/OneMovie' export default function App() { return ( <Router> <div className="container"> ... <div className="col-md-10"> <Switch> {/* <Route path="/movies/:id" render={(props) => <Movie {...props} />} /> */} <Route path="/movies/:id" component={OneMovie} /> ... </div> </div> </Router> ) } // const Movie = (props: any) => { // let { id } = props.match.params // return <h2>Movie id {id}</h2> // } ... |
次回
次回は、Go言語でバックエンドの処理を実装します。
記事まとめ
参考書籍
ソースコード
ここまで実装したソースコードを下記に記載します。
App.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
// src/App.tsx import { BrowserRouter as Router, Switch, Route, Link, useRouteMatch } from 'react-router-dom' import Home from './components/Home' import Movies from './components/Movies' import Admin from './components/Admin' import Categories from './components/Categories' import OneMovie from './components/OneMovie' export default function App() { return ( <Router> <div className="container"> <div className="row"> <h1 className="mt-3"> Go Watch a Movie! </h1> <hr className="mb-3" /> </div> <div className="row"> <div className="col-md-2"> <nav> <ul className="list-group"> <li className="list-group-item"> <Link to="/">Home</Link> </li> <li className="list-group-item"> <Link to="/movies">Movies</Link> </li> <li className="list-group-item"> <Link to="/by-category">Categories</Link> </li> <li className="list-group-item"> <Link to="/admin">Manage Catalog</Link> </li> </ul> </nav> </div> <div className="col-md-10"> <Switch> <Route path="/movies/:id" component={OneMovie} /> <Route path="/movies"> <Movies /> </Route> <Route exact path="/by-category"> <CategoryPage /> </Route> <Route exact path="/by-category/comedy" render={(props) => <Categories {...props} title={`Comedy`}/>} /> <Route exact path="/by-category/drama" render={(props) => <Categories {...props} title={`Drama`}/>} /> <Route path="/admin"> <Admin /> </Route> <Route path="/"> <Home /> </Route> </Switch> </div> </div> </div> </Router> ) } const CategoryPage = () => { let { path, url } = useRouteMatch() console.log(path, url) return ( <div> <h2>Categories</h2> <ul> <li><Link to={`${path}/comedy`}>Comedy</Link></li> <li><Link to={`${url}/drama`}>Drama</Link></li> </ul> </div> ) } |
Movies.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
// components/Movies.tsx import { Link } from 'react-router-dom' import { useEffect, useState, Fragment } from 'react' import { Movie } from '../models/movie' const Movies = () => { // 仮のデータ const tmpData = [ {id: 1, title: "The Shawshank Redemption", runtime: 142}, {id: 2, title: "Harry Potter", runtime: 175}, {id: 3, title: "The Dark Kngiht", runtime: 142}, ] const [movies, setMovies] = useState<Movie[]>([]) useEffect(() => { ( () => { setMovies(tmpData) } )() }, []) // 空配列を第二引数に渡すとマウント・アンマウント時のみ第1引数の関数を実行 return ( <Fragment> <h2>Choose a movie</h2> <ul> {movies.map((m) => { return ( <li key={m.id}> <Link to={`/movies/${m.id}`}>{m.title}</Link> </li> ) })} </ul> </Fragment> ) } export default Movies |
OneMovie.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
// components/OneMovie.tsx import { useEffect, useState, Fragment } from 'react' import { Movie } from '../models/movie' const OneMovie = (props: any) => { const [movie, setMovie] = useState<Movie>({ id: 0, title: "No Movie", runtime: 0, }) useEffect(() => { ( () => { setMovie({ id: props.match.id, title: "Some movie", runtime: 150, }) } )() }, []) return ( <Fragment> <h2>Movie: {movie.title} {movie.id}</h2> <table className="table table-compact table-striped"> <thead></thead> <tbody> <tr> <td><strong>Title:</strong></td> <td>{movie.title}</td> </tr> <tr> <td><strong>Run Time:</strong></td> <td>{movie.runtime} minutes</td> </tr> </tbody> </table> </Fragment> ) } export default OneMovie |
Categories.tsx
1 2 3 4 5 6 7 |
// components/Categories.tsx const Categories = (props: any) => { return <h2>Category: {props.title}</h2> } export default Categories |
コメントを残す
コメントを投稿するにはログインしてください。