実施条件
[React][L4] リストの項目をボタンで削除するを理解していること
環境
MacBook Pro (2.3 GHz 8コアIntel Core i9)
macOS 14.0(23A344)
Homebrew 4.3.8
gh 2.52.0
ソースコードの前に
フォルダ構成
├── package.json
├── public/
│ └── index.html
├── src/
│ ├── index.jsx
│ ├── Todo.jsx
│ └── styles.css
│
index.html
<!DOCTYPE html>
<html lang="en">
<head> </head>
<body>
<div id="root"></div>
</body>
</html>
index.jsx
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { Todo } from "./Todo";
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<StrictMode>
<Todo />
</StrictMode>
);
ソースコード
Todo.jsx(旧)
import { useState } from "react";
import "./styles.css";
export const Todo = () => {
const [todoText, setTodoText] = useState("");
const [incompleteTodos, setIncompleteTodos] = useState([
"TODOです1",
"TODOです2",
]);
const [completeTodos, setCompleteTodos] = useState([
"TODOでした1",
"TODOでした2",
]);
const onChangeTodoText = (event) => setTodoText(event.target.value);
const onClickAdd = () => {
if (todoText === "") {
return;
} else {
const newTodos = [...incompleteTodos, todoText];
setIncompleteTodos(newTodos);
setTodoText("");
}
};
const onClickDelete = (index) => {
const newTodos = [...incompleteTodos];
newTodos.splice(index, 1);
setIncompleteTodos(newTodos);
};
return (
<>
<div className="input-area">
<input
placeholder="TODOを入力"
value={todoText}
onChange={onChangeTodoText}
></input>
<button onClick={onClickAdd}>追加</button>
</div>
<div className="incomplete-area">
<p className="title">未完了のTODO</p>
<ul>
{incompleteTodos.map((todo, index) => {
return (
<li key={todo}>
<div className="list-row">
<p>{todo}</p>
<button>完了</button>
<button onClick={() => onClickDelete(index)}>削除</button>
</div>
</li>
);
})}
</ul>
</div>
<div className="complete-area">
<p className="title">完了のTODO</p>
<ul>
{completeTodos.map((todo) => {
return (
<li>
<div className="list-row">
<p>{todo}</p>
<button>戻す</button>
</div>
</li>
);
})}
</ul>
</div>
</>
);
};
Todo.jsx
import { useState } from "react";
import "./styles.css";
export const Todo = () => {
const [todoText, setTodoText] = useState("");
const [incompleteTodos, setIncompleteTodos] = useState([
"TODOです1",
"TODOです2",
]);
const [completeTodos, setCompleteTodos] = useState([
"TODOでした1",
"TODOでした2",
]);
const onChangeTodoText = (event) => setTodoText(event.target.value);
const onClickAdd = () => {
if (todoText === "") {
return;
} else {
const newTodos = [...incompleteTodos, todoText];
setIncompleteTodos(newTodos);
setTodoText("");
}
};
const onClickDelete = (index) => {
const newTodos = [...incompleteTodos];
newTodos.splice(index, 1);
setIncompleteTodos(newTodos);
};
+ const onClickComplete = () => {
+ const newIncompleteTodos = [...incompleteTodos];
+ newIncompleteTodos.splice(index, 1);
+
+ const newCompleteTodos = [...completeTodos, incompleteTodos[index]];
+
+ setIncompleteTodos(newIncompleteTodos);
+ setCompleteTodos(newCompleteTodos);
+ };
return (
<>
<div className="input-area">
<input
placeholder="TODOを入力"
value={todoText}
onChange={onChangeTodoText}
></input>
<button onClick={onClickAdd}>追加</button>
</div>
<div className="incomplete-area">
<p className="title">未完了のTODO</p>
<ul>
{incompleteTodos.map((todo, index) => {
return (
<li key={todo}>
<div className="list-row">
<p>{todo}</p>
- <button>完了</button>
+ <button onClick={() => onClickComplete(index)}>完了</button>
<button onClick={() => onClickDelete(index)}>削除</button>
</div>
</li>
);
})}
</ul>
</div>
<div className="complete-area">
<p className="title">完了のTODO</p>
<ul>
{completeTodos.map((todo) => {
return (
<li>
<div className="list-row">
<p>{todo}</p>
<button>戻す</button>
</div>
</li>
);
})}
</ul>
</div>
</>
);
};
解説
目的
-
button
タグ(完了)を押下した時に、配列incompleteTodos
の任意の要素を削除する -
button
タグ(完了)を押下した時に、配列completeTodos
の任意の要素を追加する
コーディング
-
button
タグ(完了)を押下した際に発火する、onClick
イベントを設定するTodo.jsx- <button>完了</button> + <button onClick={() => onClickComplete(index)}>完了</button>
-
onClick
イベントで呼び出される関数をonClickComplete
として設定するTodo.jsx+ const onClickComplete = () => { + };
-
配列
incompleteTodos
の任意の項目を指定するには、配列incompleteTodos
内の何番目の配列かを見分けられれば良い
=配列のindex
を取得するTodo.jsx- {incompleteTodos.map((todo) => { + {incompleteTodos.map((todo, index) => {
-
onClickComplete
にindex
を関数の引数として設定するTodo.jsx+ const onClickComplete = (index) => { + };
-
onCkick
にindex
を関数の引数として設定する
なぜ、onClick={onClickComplete(index)
と記載せずに、onClick={() => onClickComplete(index)
と記載するかはこちらを参照Todo.jsx- <button>完了</button> + <button onClick={() => onClickComplete(index))}>完了</button>
-
最新の
未完了
の配列incompleteTodos
をnewIncompleteTodos
として定義する(コピーする)Todo.jsxconst onClickComplete = () => { + const newIncompleteTodos = [...incompleteTodos];
-
splice
メソッドを使って、渡ってきた第一引数index
から第二引数1
番目を削除するTodo.jsxconst onClickComplete = () => { const newIncompleteTodos = [...incompleteTodos]; + newIncompleteTodos.splice(index, 1);
-
最新の
完了
の配列completeTodos
をnewCompleteTodos
として定義する(コピーする)Todo.jsxconst onClickComplete = () => { const newIncompleteTodos = [...incompleteTodos]; newIncompleteTodos.splice(index, 1); + const newCompleteTodos = [...completeTodos];
-
配列
newCompleteTodos
にincompleteTodos
内の任意の(index)の要素を追加するTodo.jsxconst onClickComplete = () => { const newIncompleteTodos = [...incompleteTodos]; newIncompleteTodos.splice(index, 1); + const newCompleteTodos = [...completeTodos, incompleteTodos[index]];
-
配列
newIncompleteTodos
を、配列incompleteTodos
の更新関数であるsetIncompleteTodos
に引数として渡す
=配列incompleteTodos
を更新するTodo.jsxconst onClickComplete = () => { const newIncompleteTodos = [...incompleteTodos]; newIncompleteTodos.splice(index, 1); const newCompleteTodos = [...completeTodos, incompleteTodos[index]]; + setIncompleteTodos(newIncompleteTodos);
-
配列
newIncompleteTodos
を、配列completeTodos
の更新関数であるsetCompleteTodos
に引数として渡す
=配列completeTodos
を更新するTodo.jsxconst onClickComplete = () => { const newIncompleteTodos = [...incompleteTodos]; newIncompleteTodos.splice(index, 1); const newCompleteTodos = [...completeTodos, incompleteTodos[index]]; setIncompleteTodos(newIncompleteTodos); + setCompleteTodos(newCompleteTodos); };
処理
-
incompleteTodos
に登録されている未完了のTODO
がリストとして表示されている - ユーザーが
完了
ボタンを押下する -
onClick
イベントが発火する {incompleteTodos.map((todo, index) => {...}}
によってindex
が各li
要素に渡されているので、該当の要素がわかる-
() => onClickComplete(index)
が実行される - 該当の
index
がonClickComplete
に渡る - 最新の配列
incompleteTodos
をnewIncompleteTodos
に渡す(コピーする) -
splice
メソッドを使って、newIncompleteTodos
の第一引数index
から第二引数1
番目を削除する - 最新の配列
completeTodos
をnewCompleteTodos
に渡し(コピーし)、該当のindex
を配列incompleteTodos
内の要素を追加する -
newIncompleteTodos
を更新関数setIncompleteTodos
に渡す -
incompleteTodos
が更新される - Reactの再レンダリングがトリガーされる
-
newCompleteTodos
を更新関数setCompleteTodos
に渡す -
completeTodos
が更新される - Reactの再レンダリングがトリガーされる
-
button
タグ(完了)を押下した時に、配列incompleteTodos
の任意の要素が削除・出力される