ReactとJavaScriptでAPIで記事データを取得してみた！その3

# はじめに
こんにちは！WEBエンジニア転職目指しているK.Yです！
今回は、APIでデータを取得して表示させる内容となります。

以下の記事は、手書きでデータを取得した内容となります！
良かったらこちらも読んでいただけると嬉しいです！
Reactの基本的な機能や特徴について記述しています！

https://qiita.com/ken887265/items/a6dfc1f62ee185b986b2

https://qiita.com/ken887265/items/8d6255a5aba95897ca67

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

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

## バージョン
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-router-dom": "^6.23.1"

JavaScript　ES6以降


## コード
```javascript
PostsList

import React, { useEffect, useState } from 'react'
import './App.css'
import { Link } from 'react-router-dom';

const PostsList = () => {

  const formatDate = (dateString) => {
    const date = new Date(dateString);
    const options = { year: 'numeric', month: 'numeric', day: 'numeric' };

    return date.toLocaleDateString('ja-JP', options);
  };

  const [posts, setPosts] = useState({ articles: [] });
  const [loading, setLoading] = useState(true); // ローディング状態を追加

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch("https://1hmfpsvto6.execute-api.ap-northeast-1.amazonaws.com/dev/posts");
        const data = await response.json();

        setPosts( data );

      }  finally {
        setLoading(false); // データ取得が完了したらローディングを終了
      }
    };

    fetchData();
  }, []);

  if (loading) {
    return <div>Loading...</div>; // ローディング中の表示
  }

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

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

}

export default PostsList;
```
```javascript
DetailsPage

const { id } = useParams();

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

  const [detailsData, setDetailsData] = useState();
  const [loading, setLoading] = useState(true); // ローディング状態を追加

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(`https://1hmfpsvto6.execute-api.ap-northeast-1.amazonaws.com/dev/posts/${id}`);
        const result = await response.json();

        setDetailsData(result.post); 
      } finally {
        setLoading(false); // データ取得が完了したらローディングを終了
      }

    };

    fetchData();
  }, [id]);

  if (loading) {
    return <div>Loading...</div>; // ローディング中の表示
  }

  if (!detailsData) 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 key={detailsData.id}>
              <div><img src={detailsData.thumbnailUrl} alt="img" /></div>
              <div className="date">{formatDate(detailsData.createdAt)}</div>
              <div className="programming-language">
                {detailsData.categories.map((category, idx) => (
                  <span key={idx} className="category-box">{category}</span>
                ))}
              </div>
              <div className="title">{detailsData.title}</div>
              <div style={{ display: 'block' }} className="content" dangerouslySetInnerHTML={{ __html: detailsData.content }}></div>
            </li>
          </ul>
        </div>
    </div>
  );
}
export default DetailsPage;
```

# API
APIとは、ソフトウェアアプリケーションが互いにやり取りを行うための仕組みです。
フロントエンド、バックエンドの連携において重要な役割を果たします。

APIはHTTPなどのプロトコルを用いて、リクエストやレスポンスのやり取りを行います。
リクエストには、データの取得や登録・削除・更新などの操作を支持する情報が含まれていて、
レスポンスは、データやエラーメッセージ’などの情報が含まれています。

## PostsList(記事一覧ページ)
```javascript
PostsList

import React, { useEffect, useState } from 'react'
import './App.css'
import { Link } from 'react-router-dom';

const PostsList = () => {

  const formatDate = (dateString) => {
    const date = new Date(dateString);
    const options = { year: 'numeric', month: 'numeric', day: 'numeric' };

    return date.toLocaleDateString('ja-JP', options);
  };

  const [posts, setPosts] = useState({ articles: [] });
  const [loading, setLoading] = useState(true); // ローディング状態を追加

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch("https://1hmfpsvto6.execute-api.ap-northeast-1.amazonaws.com/dev/posts");
        const data = await response.json();

        setPosts( data );

      }  finally {
        setLoading(false); // データ取得が完了したらローディングを終了
      }
    };

    fetchData();
  }, []);

  if (loading) {
    return <div>Loading...</div>; // ローディング中の表示
  }

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

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

}

export default PostsList;
```

## ステートの設定
```javascript
const [posts, setPosts] = useState({ articles: [] });
const [loading, setLoading] = useState(true); // ローディング状態を追加
```

・`useState`を使って、ステートを定義。
 `posts`は、APIから取得したデータを格納するためのステートです。
 初期値は`{ articles: [] }`（空の配列を含むオブジェクト）です。
 
・`setPosts`は、`posts`ステートを更新するための関数です。

・`loading`は、データがロード中であるかどうかを示すブール値のステートです。
 初期値は、`true`

 ・`setLoading`は、`loading`を更新するための関数です。

## データ取得（useEffectフック）
```javascript
useEffect(() => {
  const fetchData = async () => {
    try {
      const response = await fetch("https://1hmfpsvto6.execute-api.ap-northeast-1.amazonaws.com/dev/posts");
      const data = await response.json();

      setPosts(data);
    } finally {
      setLoading(false); // データ取得が完了したらローディングを終了
    }
  };

  fetchData();
}, []);
```
・`useEffect`を使って、副作用（この場合はデータ取得）を実行します。
　`useEffect`の第二引数に空の配列`[]`を渡すことで、ページ読み込み時にデータを取得したい時に、用いる手法です。

・`fetchData`は非同期関数で、APIからデータを取得。
 
・`fetch`関数を使って、指定されたURLからデータを取得。

・`await`を使って、`fetch`関数が完了するのを待つ」。

・`response.json()`を使って、取得したデータをJSON形式に変換します。

・`setPosts(data)`を使って、取得したデータを`posts`ステートに保存。

・`loading`ステートを`false`に設定します。

・`useEffect`の中で、`fetchData`関数を呼び出して、データ取得を開始します。

・`loading`ステートが`true`の間は、"Loading..."というメッセージを表示します。
　データ取得が完了して`loading`ステートが`false`になると、この部分はスキップされ、次の部分（投稿リストの表示）が実行。

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

const { id } = useParams();

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

  const [detailsData, setDetailsData] = useState();
  const [loading, setLoading] = useState(true); // ローディング状態を追加

  useEffect(() => {
    const fetchData = async () => {
      try {
        const response = await fetch(`https://1hmfpsvto6.execute-api.ap-northeast-1.amazonaws.com/dev/posts/${id}`);
        const result = await response.json();

        setDetailsData(result.post); 
      } finally {
        setLoading(false); // データ取得が完了したらローディングを終了
      }

    };

    fetchData();
  }, [id]);

  if (loading) {
    return <div>Loading...</div>; // ローディング中の表示
  }

  if (!detailsData) 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 key={detailsData.id}>
              <div><img src={detailsData.thumbnailUrl} alt="img" /></div>
              <div className="date">{formatDate(detailsData.createdAt)}</div>
              <div className="programming-language">
                {detailsData.categories.map((category, idx) => (
                  <span key={idx} className="category-box">{category}</span>
                ))}
              </div>
              <div className="title">{detailsData.title}</div>
              <div style={{ display: 'block' }} className="content" dangerouslySetInnerHTML={{ __html: detailsData.content }}></div>
            </li>
          </ul>
        </div>
    </div>
  );
}
export default DetailsPage;
```
## データ取得（useEffectフック）
```javascript
useEffect(() => {
  const fetchData = async () => {
    try {
      const response = await fetch(`https://1hmfpsvto6.execute-api.ap-northeast-1.amazonaws.com/dev/posts/${id}`);
      const result = await response.json();
      setDetailsData(result.post); 
    } finally {
      setLoading(false); // データ取得が完了したらローディングを終了
    }
  };

  fetchData();
}, [id]);
```
・`useEffect`の第二引数に`[id]`を渡すことで、`id`が変わるたびにこの副作用が再実行されます。

・ `try`ブロック内で、データの取得と処理を行います。

・`finally`ブロックで、データ取得が完了したら（成功しても失敗しても）`loading`ステートを`false`に設定します。

## ポイント
・`副作用の管理`と`APIを呼び出す処理`
`useEffect`フックを使って、`API`データを取得などの副作用を管理。

・非同期処理 `async`/`await`
非同期処理は、時間のかかる操作（例えば、ネットワークリクエストやファイルの読み書きなど）をブロックせずに実行するために使用されます。JavaScriptでは、非同期処理を行うために`async`/awaitや`Promise`を使う。
 
 ## 終わり
 今回は、APIでデータ取得を実装してみました！
 少しでもこの記事がお役に立てれば幸いです！
　次回は、問い合わせフォームの作成となります。
 お楽しみに！