React 入門
JavaScript コメント掲示板アプリの開発
コメント削除機能の実装
コメントの投稿機能が実現できたので,次は削除機能を実装してきます.
まず,コメント一つひとつに対して削除するためのボタンを配置したいので,Comment コンポーネントを次の通り編集します.このとき,deleteButton
という名前のクラスを設定しておきます.
src/components/Comment.js
import React from 'react'
const Comment = (props) => {
return (
<article>
<div className="title">
{props.comment.title}
</div>
<div className="body">
{props.comment.body}
</div>
<div className="deleteButton">
<button>
削除
</button>
</div>
</article>
)
}
export default Comment
削除ボタンは画面の右側に配置したいので,index.css にスタイルを追加します.
src/index.css(抜粋)
.deleteButton {
text-align: right;
}
画面の右側に削除ボタンが配置されました.ブラウザの横幅を変化させて配置を確認してください.
次に,削除ボタンがクリックされたことを検知できるようにします.そのために,button
要素に onClick
属性を追加し,クリック時に実行される関数名 (handleClickDeleteButton) を指定します.
src/components/Comment.js(抜粋)
<div className="deleteButton">
<button
onClick={handleClickDeleteButton}
>
削除
</button>
</div>
同じ名前の関数 (handleClickDeleteButton) を定義します.この関数ではとりあえずボタンがクリックされたことをコンソールにログとして出力する処理だけを入力しておきます.
src/components/Comment.js(抜粋)
const handleClickDeleteButton = () => {
console.log("handleClickDeleteButton");
};
コメントの削除ボタンをクリックしたことを検知できるようになりました.しかし,どのコメントのボタンを押したかが現時点では取得できていません.
Comment コンポーネントには props
の中にコメントの情報があり,その中には id の情報も含まれています.次の方法でコメントの ID を取得できました.
src/components/Comment.js(抜粋)
const handleClickDeleteButton = () => {
console.log("handleClickDeleteButton", props.comment.id);
};
上では Comment コンポーネントでコメントの ID を取得しています.実際に削除の処理は CommentListPage コンポーネントで行う必要があるので,CommentListPage コンポーネントに handleDeleteButtonClick 関数を定義します.
src/components/CommentListPage.js(抜粋)
const handleDeleteButtonClick = () => {
console.log('handleDeleteButtonClick');
};
そして,Comment コンポーネントに onDelete
という名前で関数を渡します.なお,上の handleDeleteButtonClick 関数の定義は次の commentItems の定義よりも上に入力しなければならないことに注意してください.
src/components/CommentListPage.js(抜粋)
const commentItems = results.results.map((comment) => {
return (
<Comment
key={comment.id}
comment={comment}
onDelete={handleDeleteButtonClick}
/>
)
});
Comment コンポーネントには props
で onDelete
関数が渡されたので,これを handleClickDeleteButton
関数内で実行します.
src/components/Comment.js(抜粋)
const handleClickDeleteButton = () => {
// console.log("handleClickDeleteButton", props.comment.id);
props.onDelete();
};
どのコメントに対する削除ボタンがクリックされたのかを知りたいので,Comment コンポーネントでの onDelete の呼び出し時にコメントの ID を渡します.
src/components/Comment.js(抜粋)
const handleClickDeleteButton = () => {
// console.log("handleClickDeleteButton", props.comment.id);
props.onDelete(props.comment.id);
};
CommentListPage コンポーネントでは Comment コンポーネントから渡されたコメントの ID を受け取ります.
src/components/CommentListPage.js(抜粋)
const handleDeleteButtonClick = (commentId) => {
console.log('handleDeleteButtonClick', commentId);
};
ここまでの作業の結果,どのコメントに対する削除ボタンがクリックされたかを CommentListPage コンポーネントで検知できるようになりました.続いて,実際に削除を行うための処理をコーディングします.
src/components/CommentListPage.js(抜粋)
const handleDeleteButtonClick = (commentId) => {
const newComments = [...results.results].filter((comment) => {
return comment.id !== commentId;
});
const newResults = {
"count": results.count - 1, // コメント数は1減らす
"previous": results.previous,
"next" : results.next,
"results": newComments
};
setResults(newResults);
};
上のコードの詳細について説明します.2行目の [...results.results]
はスプレッド構文です.コメントの一覧を配列に展開しています.filter
ではコメントの配列を一つずつ順番に取り出して comment
に代入し,削除対象の commentID
と等しくないものを配列にして返して newComments
オブジェクト定数を作成しています.その結果として削除対象のコメントだけが削除されたことになります.その後,5行目で newResults
オブジェクトを生成するときに,対象コメントが削除された残りのコメント配列オブジェクト newComments
を指定しています.最後に11行目の setResults()
で results
を更新した結果,画面の表示が更新されることになります.
新規にコメントを投稿した後に,ブラウザで実際に削除ボタンを押してコメントが削除できることを確認します.
削除ボタンを押したときは確認画面を表示してもよいでしょう.
src/components/CommentListPage.js(抜粋)
const handleDeleteButtonClick = (commentId) => {
if (!window.confirm("削除しますか?")) {
return;
}
const newComments = [...results.results].filter((comment) => {
return comment.id !== commentId;
});
const newResults = {
"count": results.count - 1, // コメント数は1減らす
"previous": results.previous,
"next" : results.next,
"results": newComments
};
setResults(newResults);
};
ブラウザで動作を確認します.確認画面で「OK」を押したときにだけコメントが削除できるようになりました.
ここでも少々複雑な処理になりました.現時点での CommentListPage.js 全体を示しておきます.
src/components/CommentListPage.js
import React from 'react'
import { useState } from 'react';
import Comment from './Comment';
import CreateForm from './CreateForm';
const CommentListPage = () => {
const [results, setResults] = useState({
"count":10,
"next":"http://127.0.0.1:8000/comments/?page=2",
"previous":null,
"results":[
{
"id":9,
"title":"9個目のコメント",
"body":"コメントの本文9",
"updated_at":"2023-11-21T11:20:00"
},
{
"id":10,
"title":"10個目のコメント",
"body":"コメントの本文10",
"updated_at":"2023-11-21T11:10:00"
}
]
});
const handleDeleteButtonClick = (commentId) => {
if (!window.confirm("削除しますか?")) {
return;
}
const newComments = [...results.results].filter((comment) => {
return comment.id !== commentId;
});
const newResults = {
"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, body) => {
const newComments = [...results.results]; // スプレッド構文でコメントの配列だけを取り出す
newComments.unshift({ // unshift で先頭に追加,push では最後に追加
id: Date.now(),
title: title,
body: body,
updated_at: "2024-02-25T15:30:00",
});
const newResults = {
"count": results.count + 1, // コメント数は1増やす
"previous": results.previous,
"next" : results.next,
"results": newComments, // これがコメントの配列
};
setResults(newResults); // 画面を更新
}
return (
<div className="container">
<h1>コメント一覧</h1>
{commentItems}
<CreateForm
onSubmit={handelCreateFormSubmit}
/>
</div>
)
}
export default CommentListPage
Comment.js の全体も示しておきます.
src/components/Comment.js
import React from 'react'
const Comment = (props) => {
const handleClickDeleteButton = () => {
props.onDelete(props.comment.id);
};
return (
<article>
<div className="title">
{props.comment.title}
</div>
<div className="body">
{props.comment.body}
</div>
<div className="deleteButton">
<button
onClick={handleClickDeleteButton}
>
削除
</button>
</div>
</article>
)
}
export default Comment