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 サーバに登録した後にブラウザで再読込すると,登録された内容が表示されることを確認します.
では,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にも送信されてデータベースに登録されます.
その後,ブラウザで再読み込みの処理を行うと,APIから最新の2件のデータを取得することになるので画面が次のように更新されて2件の表示になります.
次は,投稿完了後に最新の2件だけが表示されるようにします.具体的には54行目のように API にリクエストを送信して画面を更新する関数を作成し,新規投稿の完了時点(109行目)でその関数を呼び出すようにします.
src/components/CommentListPage.tsx
import 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件が表示されるようになりました.