神戸学院大学 経営学部 林坂ゼミ

React 入門トップページ

« 戻る 次へ »

React 入門

TypeScript コメント掲示板アプリの開発

新規投稿で API に POST

ここでは,新規投稿時に API サーバにも POST メソッドを送信してデータを登録するようにします.まず,ターミナル(またはコマンドプロンプト)から curl コマンドで POST メソッドを送信する方法を確認します.次の通り,-X POST でメソッドを指定し,-d オプションを使ってタイトルや本文の情報を指定します.

% curl -X POST -d "title=API" -d "body=test" http://127.0.0.1:8000/comments/ ⏎

上のコマンドで API サーバに登録した後にブラウザで再読込すると,登録された内容が表示されることを確認します.

ts-2024-42

では,CommentListPage コンポーネントで新規投稿処理を行っている関数内で,Axios によってデータを送信します.

src/components/CommentListPage.tsx(抜粋)const handelCreateFormSubmit = (title: string, body: string) => {
  const newComments = [...results.results];  // スプレッド構文でコメントの配列だけを取り出す
  newComments.unshift({   // unshift で先頭に追加,push では最後に追加
    id: Date.now(),
    title: title,
    body: body,
    updated_at: "2024-03-18T12:00:00",
  });
  const newResults = {
    "count": results.count + 1, // コメント数は1増やす
    "previous": results.previous,
    "next" : results.next,
    "results": newComments,   // これがコメントの配列
  };
  setResults(newResults); // 画面を更新

  const url = "http://127.0.0.1:8000/comments/";
  axios
    .post(url, {
      title: title,
      body: body,
    })
    .then(res => {
      console.log(res.data);
    })
    .catch(err => {
      setResults(results);
      alert("投稿エラー!未入力または文字数超過です.");
    });
}

ブラウザから投稿すると,画面上でコメントが追加され3件の表示になった後,APIにも送信されてデータベースに登録されます.

ts-2024-43

その後,ブラウザで再読み込みの処理を行うと,APIから最新の2件のデータを取得することになるので画面が次のように更新されて2件の表示になります.

ts-2024-44

次は,投稿完了後に最新の2件だけが表示されるようにします.具体的には54行目のように API にリクエストを送信して画面を更新する関数を作成し,新規投稿の完了時点(109行目)でその関数を呼び出すようにします.

src/components/CommentListPage.tsximport React from 'react'
import { useState, useEffect } from 'react';
import axios, { AxiosResponse } from 'axios';
import Comment from './Comment';
import CreateForm from './CreateForm';

interface CommentData {
  id: number;
  title: string;
  body: string;
  updated_at: string;
}

interface Results {
  count: number;
  next: string | null;
  previous: string | null;
  results: CommentData[];
}

const CommentListPage: React.FC = () => {

  const [isPrevButtonDisabled, setPrevButtonDisabled] = useState<boolean>(true);
  const [isNextButtonDisabled, setNextButtonDisabled] = useState<boolean>(false);

  const [results, setResults] = useState<Results>({
    "count":10,
    "next":"http://127.0.0.1:8000/comments/?page=2",
    "previous":null,
    "results":[
      {
        "id":9999,
        "title":"ダミー",
        "body":"ダミーの本文9",
        "updated_at":"2023-11-21T11:20:00"
      },
      {
        "id":10000,
        "title":"ダミーのタイトル",
        "body":"ダミーの本文10",
        "updated_at":"2023-11-21T11:10:00"
      }
    ]
  });

  useEffect(() => {
    const url = "http://127.0.0.1:8000/comments/";
    axios.get<Results>(url)
        // .then((res: AxiosResponse<Results>) => console.log(res.data))
        .then((res: AxiosResponse<Results>) => setResults(res.data))
        .catch(err => console.log(err.message));
  }, []);

  const handleCommentsListChange = (url: string) => {
    axios.get<Results>(url)
        .then((res: AxiosResponse<Results>) => setResults(res.data))
        .catch(err => console.log(err.message));
  }

  const handleDeleteButtonClick = (commentId: number) => {
    if (!window.confirm("削除しますか?")) {
      return;
    }
    const newComments = [...results.results].filter((comment) => {
      return comment.id !== commentId;
    });
    const newResults: Results = {
      "count": results.count - 1, // コメント数は1減らす
      "previous": results.previous,
      "next" : results.next,
      "results": newComments
    };
    setResults(newResults);
  };

  const commentItems = results.results.map((comment) => {
    return (
      <Comment
        key={comment.id}
        comment={comment}
        onDelete={handleDeleteButtonClick}
      />
    )
  });

  const handelCreateFormSubmit = (title: string, body: string) => {
    const newComments = [...results.results];  // スプレッド構文でコメントの配列だけを取り出す
    newComments.unshift({   // unshift で先頭に追加,push では最後に追加
      id: Date.now(),
      title: title,
      body: body,
      updated_at: "2024-03-18T12:00:00",
    });
    const newResults = {
      "count": results.count + 1, // コメント数は1増やす
      "previous": results.previous,
      "next" : results.next,
      "results": newComments,   // これがコメントの配列
    };
    setResults(newResults); // 画面を更新

    const url = "http://127.0.0.1:8000/comments/";
    axios
      .post(url, {
        title: title,
        body: body,
      })
      .then(res => {
        handleCommentsListChange(url);
      })
      .catch(err => {
        setResults(results);
        alert("投稿エラー!未入力または文字数超過です.");
      });
  }

  const handlePrevPageButton = () => {
    if (results.previous === null) {
      return;
    }
    axios.get<Results>(results.previous!)
        .then((res: AxiosResponse<Results>) => setResults(res.data))
        .catch(err => console.log(err.message));
  };

  const handleNextPageButton = () => {
    if (results.next === null) {
      return;
    }
    axios.get<Results>(results.next!)
        .then((res: AxiosResponse<Results>) => setResults(res.data))
        .catch(err => console.log(err.message));
  };

  useEffect(() => {
    setPrevButtonDisabled(results.previous === null);
    setNextButtonDisabled(results.next === null);
  }, [results]);

  return (
    <div className="container">
      <h1>コメント一覧</h1>

      <div className="commentsHeader">
        <div>コメント総数:{results.count}</div>
        <div>
          <button
            onClick={handlePrevPageButton}
            disabled={isPrevButtonDisabled}
          >
            前のページ
          </button>
          <button
            onClick={handleNextPageButton}
            disabled={isNextButtonDisabled}
          >
            次のページ
          </button>
        </div>
      </div>

      {commentItems}

      <CreateForm
        onSubmit={handelCreateFormSubmit}
      />
    </div>
  )
}

export default CommentListPage

もう一度投稿すると,画面が更新されて最新の2件が表示されるようになりました.

ts-2024-45

目次に戻る