LoginSignup
8

More than 1 year has passed since last update.

【React初心者】 #3 ルーティング・ページ遷移を作る! react-router-dom

Last updated at Posted at 2021-01-18

Reactでreact-router-domのルーティング設定

3回目の今回は、Reactでreact-router-dom
を使用したルーティングを使います。
URLを変えることで、表示するページを変える(レンダリングする)ことができます。

前回までで、

  • Material-UIで最低限のデザインを一から作成
  • データをAPIから取得して表示

しました。コードは前回の続きとなります。

:pushpin: React & Material UI & Django RestFramework記事一覧

内容
part1 React Material UIを使ってみる
part2 【初心者】#2 React axiosでAPI データ取得
part3 【React】 #3 ルーティング・ページ遷移 react-router-dom ← ココ
part4 【Python】 Django REST Framework ReactとAPIサーバー繋ぐ

やること

  • react-router-domでのReactでのルーティング
  • ルーティングするためのリンクの付け方

react-router-dom導入

まず、インストールが必要です。
今回はreact-router-dom: 5.2.0を使用しています。

$ npm install react-router-dom

TypeScriptの場合はnpm install react-router-dom @types/react-router-domみたいです。

とりあえず、aboutページを作成します。
ただ、文字を表示するだけにします。

ということで、Aboutコンポーネントを作成。

$ touch src/components/About.js

文字を表示するだけになります。

src/components/About.js
import React from 'react'

function About() {
    return (
        <div>
            aboutページ
        </div>
    )
}

export default About

前回までのコードにRouterとSwitch、Routeを足して、
URLによって、レンダリングするコンポーネントを変えています。

material-react/src/App.js
import './App.css';
import { Grid } from '@material-ui/core';
import Header from './components/Header';
import Content from './components/Content';
import About from './components/About';

import {
  BrowserRouter as Router,
  Switch,
  Route
} from "react-router-dom";

function App() {
  return (
    <Grid container direction="column">
      <Grid item>
        <Header />
      </Grid>
      <Grid item container>
        <Grid sm={2} />
        <Grid xs={12} sm={8}>
        <Router>
          <Switch>
            <Route exact path="/">
              <Content />
            </Route>
            <Route path="/about">
              <About />
            </Route>
          </Switch>
        </Router>
        </Grid>
        <Grid sm={2} />
      </Grid>
    </Grid>
  );
}

export default App;

http://localhost:3000/about

とURLに入れると、

スクリーンショット 2021-01-16 5.25.38.png

文字だけのAboutコンポーネントが表示されています。

説明と注意

<Route exact path="/">exactは完全に一致しないとダメという意味です。

一致するURLを上から探すのだけど、このexactをつけないと、http://localhost:3000/jasdklfsとか意味ないものやでもContent`コンポーネントを使うことになります。

というか、http://localhost:3000/移行にURLに、何があろうがContentコンポーネントを使います。

試しに、exactを消して/aboutにアクセスしてみれば、Contentコンポーネントが出るので、試してみてもいいと思います。
<Route path="/"> 変更後、
http://localhost:3000/jasdklfshttp://localhost:3000/about
と書いてみましょう。

どちらも、Contentコンポーネントの内容が表示されます。
「書く順番」を考えましょう。

URLのパラメータを取得して個別記事表示

前回、JSON Placeholderからデータを取得しました。

APIをみると、個別記事の取得もできるので、
記事についている「詳細を見る」ボタンを押したら遷移させます。

個別記事のコンポーネントを作成

前回までで、左側は作りました。
今回は、右側を作ります。
個別記事IDにアクセスしたときにPostContentコンポーネントをレンダリングすることになります。
image.png

$ touch src/components/PostContent.js

/post/1だったら記事1を表示するようにしてみます。とりあえず
idを受け取れるかテスト。
/post/1にアクセスしたらuseParams(){id: 1}のようなデータが入るのでidだけをとるためconst { id }にしています。

そして、個別記事の後に受け取ったIDをくっつけて表示します。

material-react/src/components/PostContent.js
import React from 'react'
import { useParams } from 'react-router-dom'

function PostContent() {
    const { id } = useParams();
    return (
        <div>
            <h1>個別記事 {id}</h1>
        </div>
    )
}

export default PostContent

スクリーンショット 2021-01-16 6.02.07.png

http://localhost:3000/post/1にアクセスしたら1が表示されているのでOK
http://localhost:3000/post/10とかにしたら10に変わりますので試してみてください。

個別記事データをAPIで取得

Getで取得しましょう。URLの最後に記事のIDを入れればデータを取得できます。

では、JSON PlaceholderでAPIのURLは

https://jsonplaceholder.typicode.com/posts/1

{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere repellat provident occaecati excepturi optio reprehenderit",
  "body": "quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto"
}

axiosを使ったAPIのデータ取得は前回やったので簡単に書きます。

material-react/src/components/PostContent.js
import React, {useState, useEffect} from 'react';
import axios from 'axios';
import { useParams } from 'react-router-dom'

import { Grid } from '@material-ui/core'
import BodyCard from './BodyCard'


const cardContent = 
    {
        avatarUrl: "https://joeschmoe.io/api/v1/random",
        imageUrl: "https://picsum.photos/150"
    }

function PostContent() {
    const { id } = useParams();
    const [post, setPosts] = useState([])

    useEffect(() => {
        axios.get(`https://jsonplaceholder.typicode.com/posts/${id}`)
        .then(res => {
            setPosts(res.data)
        })
    }, [])

    return (
        <Grid container spacing={2}>
            <Grid item xs={12} key={post.id}>
                <BodyCard {...{...post, ...cardContent}} />
            </Grid>
        </Grid>
    )
}

export default PostContent
  • https://jsonplaceholder.typicode.com/posts/${id}でURLから取得したパラメータを埋め込んでGET、APIでの取得
  • useStateを使って、ページを開いた時一度だけ、APIで取得
  • 前回作った、BodyCardコンポーネントに渡してMaterial-UIのカードの形式で作成
    • {...{...post, ...cardContent}}で二つのオブジェクトを結合して渡してます

とりあえず、ページを作り込むより、ルーティングの使い方説明メインなので、

  • 詳細をみるというボタンがあったり、
  • 画像がAPIでランダム取得するので変わってしまう、
  • 画像がhight: 150pxだと大きさおかしい

とかツッコミありますができました。
スクリーンショット 2021-01-16 16.44.21.png

一応、前回の記事から変更はないですが、BodyCardコンポーネントも載せておきます。

BodyCard.js
import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import CardHeader from '@material-ui/core/CardHeader';
import Avatar from '@material-ui/core/Avatar';
import IconButton from '@material-ui/core/IconButton';
import StarBorderOutlinedIcon from '@material-ui/icons/StarBorderOutlined';
import { CardMedia } from '@material-ui/core';

const useStyles = makeStyles({
    bullet: {
      display: 'inline-block',
      margin: '0 2px',
      transform: 'scale(0.8)',
    },
    title: {
      fontSize: 14,
    },
    pos: {
      marginBottom: 12,
    },
    cHeader: {
        height: '50px',
        overflow: 'hidden',
        textOverflow: 'ellipsis',
        whiteSpace: 'nowrap',
        "& .MuiCardHeader-content": {
            overflow: 'hidden'
        }
    },
    cContent: {
        height: '200px',
        overflow: 'hidden'
    }
});


function BodyCard(props) {
    const { userId, id, title, body, avatarUrl, imageUrl } = props;
    const classes = useStyles();
    const bull = <span className={classes.bullet}></span>;
    return (
        <Card variant="outlined">
            <CardHeader
                avatar={<Avatar src={avatarUrl} />}
                action={
                <IconButton aria-label="settings">
                    <StarBorderOutlinedIcon />
                </IconButton>
                }
                className={classes.cHeader}
                title={title}
            />
            <CardMedia style={{ height: "150px" }} image={imageUrl} />
            <CardContent className={classes.cContent}>
            <Typography variant="body2" component="p">
                {body}
            </Typography>
            </CardContent>
            <CardActions>
            <Button size="small">詳細をみる</Button>
            </CardActions>
        </Card>
    );
}

export default BodyCard

ボタンにリンクをつける

Material-UIにはButtonにhrefつければ遷移できるようになります。
以下のようにすれば大丈夫。

BodyCard.js
<Button size="small" href={`/post/${id}`}>詳細をみる</Button>

今回、Buttonにリンク機能がついていたので使いましたが、
デザインを自分でやりたい、文字にリンクつけるなどの場合は、

<Link to="/post/1">記事1の詳細</Link>

<Link to=`/post/${id}`>記事{$id}の詳細</Link>

などにすればいいと思います。
react-routerの公式サイトは英語ですが、
コードがみやすいのでみながら試してみてください。

https://reactrouter.com/web/example/basic

まとめ

  • react-router-domでのReactでのルーティング
  • ルーティングするためのリンクの付け方

をやりました。次回はAPIサーバーをPythonのDjangoで自作します。

補足

これまで、RouterをApp.jsに書きました。
ただ、ヘッダーがいらない、デザインを変えたいなどの場合(ログインページ、LP、認証のためのURLとか使う場合など)、

index.jsにrouterを書くこともできるので補足として。

index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import About from './components/About';
import reportWebVitals from './reportWebVitals';
import { Route, BrowserRouter } from "react-router-dom";

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <div>
        <Route exact path="/" component={App} />
        <Route exact path="/about" component={About} />
      </div>
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

:pushpin: React & Material UI & Django RestFramework記事一覧

内容
part1 React Material UIを使ってみる
part2 【初心者】#2 React axiosでAPI データ取得
part3 【React】 #3 ルーティング・ページ遷移 react-router-dom ← ココ
part4 【Python】 Django REST Framework ReactとAPIサーバー繋ぐ

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8