前回までの記事 アプリ開発を楽しむ【#1:アプリの概要】 アプリ開発を楽しむ【#2:環境構築1(React+TypeScript)】 アプリ開発を楽しむ【#3:環境構築2 (ESLint+Prettier)】 アプリ開発を楽しむ【#4:ヘッダー】 アプリ開発を楽しむ【#5:MyTogoList】 アプリ開発を楽しむ【#6:Reduxで状態管理1】 アプリ開発を楽しむ【#7:Reduxで状態管理2】 アプリ開発を楽しむ【#8:Google Map API】 アプリ開発を楽しむ【#9:新規追加モーダルUI(togo)】 アプリ開発を楽しむ【#10:Google Mapから座標を取得】 アプリ開発を楽しむ【#11:Togoの追加】 アプリ開発を楽しむ【#12:Togoの編集1】 アプリ開発を楽しむ【#13:Togoの編集2】 アプリ開発を楽しむ【#14:カスタムフック】 アプリ開発を楽しむ【#15:ルーティングの設定】 アプリ開発を楽しむ【#16:Mapページをつくる】 アプリ開発を楽しむ【#17:投稿一覧ページ他をつくる】

今回は、投稿詳細ページをつくっていきたいと思います。 完成イメージは次のとおりです。 スクリーンショット 2022-10-13 17.28.27.png

今回やること

  1. PostDetailコンポーネントの作成
  2. OnePostページにPostDetailを表示

1.PostDetailコンポーネントの作成

frontend/app/src/components/post/PostDetail.tsxを次のようにつくります。

import React, { useEffect } from 'react';
import { Box, Chip, Paper, Grid, Typography } from '@mui/material';
import { useDispatch, useSelector } from 'react-redux';
import { getPostList, initialState } from '../../redux/postSlice';
import { RootState } from '../../redux/store';
import Error from '../Error';
import samplePostList from '../../sampleData/posts';

interface PostDetail {
  togoId: number;
}

const PostDetail: React.FC<PostDetail> = ({ togoId }) => {
  const dispatch = useDispatch();
  const { postList } = useSelector((state: RootState) => state.post || initialState);

  useEffect(() => {
    dispatch(getPostList(samplePostList));
  }, [dispatch]);

  const post = postList.filter((item) => item.togo.id === togoId);

  if (!post.length) {
    return <Error />;
  }

  return (
    <>
      <Paper
        sx={{
          position: 'relative',
          backgroundColor: 'grey.300',
          color: '#fff',
          mb: 4,
          backgroundSize: 'cover',
          backgroundRepeat: 'no-repeat',
          backgroundPosition: 'center',
          backgroundImage: `url(${
            post[0].image ? post[0].image : 'https://source.unsplash.com/random'
          })`,
        }}
      >
        <Box
          sx={{
            position: 'absolute',
            top: 0,
            bottom: 0,
            right: 0,
            left: 0,
          }}
        />
        <Grid container>
          <Grid item md={6}>
            <Box
              sx={{
                position: 'relative',
                height: { xs: '250px', md: '350px' },
              }}
            />
          </Grid>
        </Grid>
      </Paper>
      <Typography variant="h6" gutterBottom sx={{ fontWeight: 'bold' }}>
        {post[0].title}
      </Typography>
      <Box component="div" sx={{ display: 'inline' }}>
        {post[0].togo.location}
      </Box>
      <Box component="div" sx={{ display: 'inline', ml: 1 }}>
        <Chip label={post[0].togo.tag} variant="outlined" size="small" />
      </Box>
      <Box component="div" sx={{ display: 'flex' }}>
        投稿日:{post[0].publishedAt}
      </Box>
      <Box component="div" sx={{ mt: 2 }}>
        {post[0].description}
      </Box>
    </>
  );
};

export default PostDetail;

受け取ったpropsのtogoidを元にStoreのpostListから該当のpostを表示させています。

2.OnePostページにPostDetailを表示

import { Container } from '@mui/material';
import { useParams } from 'react-router-dom';
import PostDetail from './post/PostDetail';

const OnePost = () => {
  const { id } = useParams();

  return (
    <Container maxWidth="lg" sx={{ pt: 2 }}>
      <PostDetail togoId={Number(id)} />
    </Container>
  );
};

export default OnePost;

useParamsを使うとURLからidを取得できるようになります。 App.tsxで設定している下記の部分で「:id」としているのでconst { id } = useParams()で取得できます。

<Route path="/posts/:id" element={<OnePost />} />

もし、App.tsxで次のように書いていたら、const { post_id } = useParams()で取得ができます。

<Route path="/posts/:post_id" element={<OnePost />} />

そして、URLから取得したidをPostDetailコンポーネントにpropsとして渡しています。

今回は以上となります。 これで、投稿一覧から投稿詳細ページへ、Mapページのマーカーから投稿詳細ページにページ遷移できるようになりました。

コードはGitHubに置いてありますのでよければ参考にしてください。 mainブランチは常に最新のものになります。 今回の内容はblog_18のブランチを参照してください。 https://github.com/KINE-M/togo_app