LoginSignup
0
0

ReactとJavaScriptで記事詳細ページの作成してみた!その2

Last updated at Posted at 2024-06-20

はじめに

こんにちは!WEBエンジニア転職目指しているK.Yです!
コメントいただけると嬉しいです:sunglasses:

前回は、記事一覧を作成した内容でした!
今回は、記事詳細ページの作成となります!

Reactの基本的な機能と特徴を理解するのに役立つ要素がありますので、
Reactこれから学ぶ方にも良いインプットになってくれれば幸いです!

前回の記事と重複している部分は省略しております!!

対象者

・ React初心者
・ フロントエンドに興味がある人

バージョン

"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.23.1"

JavaScript ES6以降

記事詳細ページ

スクリーンショット 2024-06-20 20.00.51.png

コード

DetailsPage.js

import { Link, useParams } from 'react-router-dom';
import './App.css';
import { posts } from './data/posts';

const DetailsPage = () => {

  const { id } = useParams();
  const post = posts.find(post => post.id === parseInt(id));

  const formatDate = (dateString) => {
    const date = new Date(dateString);
    const options = { year: 'numeric', month: 'numeric', day: 'numeric' };
    return date.toLocaleDateString('ja-JP', options);
  }

  if (!post) return <div>投稿が見つかりません</div>;

  return (
    <div className='App'>
      <header className="header-App">
        <Link to="/" className="link">Blog</Link>
        <Link to="/" className="link" >お問い合わせ</Link>
      </header>

      <div style={{ border: 'none' }} className="posts-info">
        <ul>
          <li>
            <div><img src={post.thumbnailUrl} alt="img" /></div>
            <div className="date">{formatDate(post.createdAt)}</div>
            <div className="programming-language">
              {post.categories.map((category, idx) => (
                <span key={idx} className="category-box">{category}</span>
              ))}
            </div>
            <div className="title">{post.title}</div>
            <div style={{ display: 'block' }} className="content" dangerouslySetInnerHTML={{ __html: post.content }}></div>
          </li>
        </ul>
      </div>
    </div>
  );
}

export default DetailsPage;

PostsList.js

mport React from 'react'
import './App.css'
import { Link } from 'react-router-dom';
import { posts } from './data/posts';


const PostsList = () => {

  // formatDate 関数を定義し、日付文字列をフォーマットする処理を関数内で行う
  const formatDate = (dateString) => {

    // '2023-09-11T09:00:00.000Z'を Dateオブジェクトに変換
    const date = new Date(dateString);

    // 年、月、日を数値形式で表示するためのオプションで定義する
    const options = { year: 'numeric', month: 'numeric', day: 'numeric' };

    // `Date` オブジェクトをローカルの日付文字列に変換
    return date.toLocaleDateString('ja-JP', options);
  };

  return (
    <div className="App">
      <header className="header-App">
        <Link className="link" to="/">Blog</Link>
        <Link className="link" to="/">お問い合わせ</Link>
      </header>

      {
        posts.map((elem) => (

          <div key={elem.id} className="posts-info">
            <ul >
              <li>
                <Link to={`/post/${elem.id}`}>
                  <div className="date">{formatDate(elem.createdAt)}</div>
                  <div className="programming-language">{elem.categories.map((category, idx) => (
                    <span key={idx} className="category-box">{category}</span>
                  ))}</div>
                  <div className="title">{elem.title}</div>
                  <div className="content" dangerouslySetInnerHTML={{ __html: elem.content }}>
                  </div>
                </Link>
              </li>
            </ul>
          </div>
        ))}
    </div>

  );

}

export default PostsList ;

App.js

import React from 'react';
import './App.css';
import { Routes, Route } from 'react-router-dom';
import PostsList from './PostsList';
import DetailsPage from './DetailsPage';

const App = () => {
  return (
    <Routes>
      <Route path="/" element={<PostsList />} />
      <Route path="/post/:id" element={<DetailsPage  />} />
    </Routes>
  );
};
posts.js

export const posts = [
  {
    id: 1,
    title: 'APIで取得した記事タイトル1',
    thumbnailUrl: 'https://placehold.jp/800x400.png',
    createdAt: '2023-09-11T09:00:00.000Z',
    categories: ['React', 'TypeScript'],
    content: `
    本文です。本文です。本文です。本文です。本文です。本文です。<br/>本文です。本文です。本文です。本文です。本文です。<br/><br/>本文です。本文です。本文です。本文です。本文です。本文です。本文です。本文です。本文です。<br/><br/><br/>本文です。本文です。本文です。本文です。本文です。本文です。<br/>`,
  },
  {
    id: 2,
    title: 'APIで取得した記事タイトル2',
    thumbnailUrl: 'https://placehold.jp/800x400.png',
    createdAt: '2023-09-10T09:00:00.000Z',
    categories: ['HTML', 'CSS'],
    content: `
    本文です。本文です。本文です。本文です。本文です。本文です。<br/>本文です。本文です。本文です。本文です。本文です。<br/><br/>本文です。本文です。本文です。本文です。本文です。本文です。本文です。本文です。本文です。<br/><br/><br/>本文です。本文です。本文です。本文です。本文です。本文です。<br/>`,
  },
  {
    id: 3,
    title: 'APIで取得した記事タイトル3',
    thumbnailUrl: 'https://placehold.jp/800x400.png',
    createdAt: '2023-09-09T09:00:00.000Z',
    categories: ['JavaScript'],
    content: `
    本文です。本文です。本文です。本文です。本文です。本文です。<br/>本文です。本文です。本文です。本文です。本文です。<br/><br/>本文です。本文です。本文です。本文です。本文です。本文です。本文です。本文です。本文です。<br/><br/><br/>本文です。本文です。本文です。本文です。本文です。本文です。<br/>`,
  },
]

DetailsPage.js (詳細記事ページ)

import { Link, useParams } from 'react-router-dom';
import './App.css';
import { posts } from './data/posts';

import { Link, useParams } from 'react-router-dom';
Linkは、ReactRouterで定義されたリンクを作成するためのコンポーネント。
 これにより、ページ間のナビゲーションが可能となります。

useParams
useParamsは、URLのパラメーターを取得するためのフック。
 フックとは、関数コンポーネントで状態管理や副作用を扱うためのツール。
 フックは、トップレベルで呼び出すというルールがあり、ブッロク内部等から呼び出せない。

import { posts } from './data/posts';
 postsデータをインポートしています。
 このデータは投稿詳細を表示するために使用します。

コンポーネントの定義

const DetailsPage = () => {

const DetailsPage = () => { DetailsPage関数コンポーネントを定義しています。
 特定の詳細のページを表示させます。

urlパラメータの取得

const { id } = useParams();
const post = posts.find(post => post.id === parseInt(id));

useParamsフックを使って、URLからidのパラメータを取得します。
const post = posts.find(post => post.id === parseInt(id));
 posts配列の中から、idに一致する投稿を検索します。
 parseInt(id)を使って、文字列として取得したidを整数に変換する。
  一致する投稿が見つかると、postへ格納する。

投稿が見つからなかった場合の処理

if (!post) return <div>投稿が見つかりません</div>;

if (!post) return <div>投稿が見つかりません</div>;
一致する投稿が見つからなかった場合、投稿が見つかりませんというメッセージを表示させる。

投稿の詳細を表示

<header className="header-App">
      return (
  <div className='App'>
    <header className="header-App">
      <Link to="/" className="link">Blog</Link>
      <Link to="/" className="link" >お問い合わせ</Link>
    </header>

    <div style={{ border: 'none' }} className="posts-info">
      <ul>
        <li>
          <div><img src={post.thumbnailUrl} alt="img" /></div>
          <div className="date">{formatDate(post.createdAt)}</div>
          <div className="programming-language">
            {post.categories.map((category, idx) => (
              <span key={idx} className="category-box">{category}</span>
            ))}
          </div>
          <div className="title">{post.title}</div>
          <div style={{ display: 'block' }} className="content" dangerouslySetInnerHTML={{ __html: post.content }}></div>
        </li>
      </ul>
    </div>
  </div>
);
</header>

<header className="header-App">
ヘッダー部分を定義。
Linkコンポーネントを使って、ブログとお問い合わせへのリンクを作成しています。

<div style={{ border: 'none' }} className="posts-info">
投稿の詳細情報を表示するdiv要素です。
style={{ border: 'none' }}はスタイルを直接設定しています。

<div><img src={post.thumbnailUrl} alt="img" /></div>
投稿のサムネイル画像を表示している。

<div className="programming-language"> {post.categories.map((category, idx) => ( <span key={idx} className="category-box">{category}</span> ))} </div>
投稿カテゴリ(プログラミング言語)を表示させます。
post.categories配列をループして各カテゴリを表示します。

PostsList.js (記事一覧ページ)

PostsList.js

mport React from 'react'
import './App.css'
import { Link } from 'react-router-dom';
import { posts } from './data/posts';


const PostsList = () => {

  // formatDate 関数を定義し、日付文字列をフォーマットする処理を関数内で行う
  const formatDate = (dateString) => {

    // '2023-09-11T09:00:00.000Z'を Dateオブジェクトに変換
    const date = new Date(dateString);

    // 年、月、日を数値形式で表示するためのオプションで定義する
    const options = { year: 'numeric', month: 'numeric', day: 'numeric' };

    // `Date` オブジェクトをローカルの日付文字列に変換
    return date.toLocaleDateString('ja-JP', options);
  };

  return (
    <div className="App">
      <header className="header-App">
        <Link className="link" to="/">Blog</Link>
        <Link className="link" to="/">お問い合わせ</Link>
      </header>

      {
        posts.map((elem) => (

          <div key={elem.id} className="posts-info">
            <ul >
              <li>
                <Link to={`/post/${elem.id}`}>
                  <div className="date">{formatDate(elem.createdAt)}</div>
                  <div className="programming-language">{elem.categories.map((category, idx) => (
                    <span key={idx} className="category-box">{category}</span>
                  ))}</div>
                  <div className="title">{elem.title}</div>
                  <div className="content" dangerouslySetInnerHTML={{ __html: elem.content }}>
                  </div>
                </Link>
              </li>
            </ul>
          </div>
        ))}
    </div>

  );

}

export default PostsList ;

投稿リストのレンダリング

{
  posts.map((elem) => (
    <div key={elem.id} className="posts-info">
      <ul >
        <li>
          <Link to={`/post/${elem.id}`}>
            <div className="date">{formatDate(elem.createdAt)}</div>
            <div className="programming-language">{elem.categories.map((category, idx) => (
              <span key={idx} className="category-box">{category}</span>
            ))}</div>
            <div className="title">{elem.title}</div>
            <div className="content" dangerouslySetInnerHTML={{ __html: elem.content }}></div>
          </Link>
        </li>
      </ul>
    </div>
  ))
}

<div key={elem.id} className="posts-info">
各投稿をdiv要素で囲み、key属性にはユニークなidを設定しています。

Linkコンポーネントを使って、各投稿の詳細ページへのリンクを作成します。
to={/post/${elem.id}}は、投稿のidに基づいたURLを表示させます。

App.js (アプリを起動するエントリーポイント)

App.js

import React from 'react';
import './App.css';
import { Routes, Route } from 'react-router-dom';
import PostsList from './PostsList';
import DetailsPage from './DetailsPage';

const App = () => {
  return (
    <Routes>
      <Route path="/" element={<PostsList />} />
      <Route path="/post/:id" element={<DetailsPage  />} />
    </Routes>
  );
};

App.js
 このファイルは、Reactアプリを実行する際、一番早く呼び出させるファイル(エントリーポイント)です!

import DetailsPage from './DetailsPage';
PostsList(記事一覧ページ)と同じ様に、DetailsPageもインポートさせます。

ルーティングの設定

return (
  <Routes>
    <Route path="/" element={<PostsList />} />
    <Route path="/post/:id" element={<DetailsPage  />} />
  </Routes>
);

<Route path="/post/:id" element={<DetailsPage />} />
 path="/post/:id"は、特定の投稿idに基づいたパスです。
idはパスパラメータで、任意のidを受け取ることができます。
このパスにアクセルすると、DetailsPageコンポーネントが表示させます。

ポイント

React Routerを使ったルーティング
react-router-domは、Reactアプリケーションでルーティングを管理するもの。

フック(Hooks)
Reactのフックは、関数パラメータの状態管理や副作用処理を行う機能。
今回は、useParamsというフックを使って、URLパラメータを取得しました。

条件付きレンダリング
Reactでは、条件に基づいてコンポーネントや要素をレンダリングすることができます。

まとめ

今回は、記事詳細ページ作成についての内容でした
次回は、APIで記事データを取得し表示させるという内容です!
今までは、手書きでデータを表示させましたが、次はAPIからデータを取得し、
表示させるという内容です!

お楽しみに!

0
0
0

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
0
0