実施条件
[React][L5] リストの項目をボタンで削除・出力するを理解していること
環境
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);
};
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 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>
</>
);
};
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);
};
+ const onClickBack = (index) => {
+ const newCompleteTodos = [...completeTodos];
+ newCompleteTodos.splice(index, 1);
+
+ const newIncompleteTodos = [...incompleteTodos, completeTodos[index]];
+
+ setCompleteTodos(newCompleteTodos);
+ setIncompleteTodos(newIncompleteTodos);
+ };
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 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) => {
+ {completeTodos.map((todo, index) => {
return (
<li>
<div className="list-row">
<p>{todo}</p>
- <button>戻す</button>
+ <button onClick={() => onClickBack(index)}>戻す</button>
</div>
</li>
);
})}
</ul>
</div>
</>
);
};
解説
目的
-
buttonタグ(戻す)を押下した時に、配列completeTodosの任意の要素を削除する -
buttonタグ(戻す)を押下した時に、配列incompleteTodosの任意の要素を追加する
コーディング
-
buttonタグ(戻す)を押下した際に発火する、onClickイベントを設定するTodo.jsx- <button>完了</button> + <button onClick={() => onClickBack(index)}>完了</button> -
onClickイベントで呼び出される関数をonClickBackとして設定するTodo.jsx+ const onClickBack = () => { + }; -
配列
completeTodosの任意の項目を指定するには、配列completeTodos内の何番目の配列かを見分けられれば良い
=配列のindexを取得するTodo.jsx- {completeTodos.map((todo) => { + {completeTodos.map((todo, index) => { -
onClickBackにindexを関数の引数として設定するTodo.jsx+ const onClickBack = (index) => { + }; -
onCkickにindexを関数の引数として設定する
なぜ、onClick={onClickBack(index)と記載せずに、onClick={() => onClickBack(index)と記載するかはこちらを参照Todo.jsx- <button>完了</button> + <button onClick={() => onClickBack(index))}>完了</button> -
最新の
未完了の配列completeTodosをnewCompleteTodosとして定義する(コピーする)Todo.jsxconst onClickBack = () => { + const newCompleteTodos = [...completeTodos]; -
spliceメソッドを使って、渡ってきた第一引数indexから第二引数1番目を削除するTodo.jsxconst onClickBack = () => { const newCompleteTodos = [...completeTodos]; + newCompleteTodos.splice(index, 1); -
最新の
完了の配列incompleteTodosをnewIncompleteTodosとして定義する(コピーする)Todo.jsxconst onClickBack = () => { const newCompleteTodos = [...completeTodos]; newCompleteTodos.splice(index, 1); + const newIncompleteTodos = [...incompleteTodos]; -
配列
incompleteTodosにcompleteTodos内の任意の(index)の要素を追加するTodo.jsxconst onClickBack = () => { const newCompleteTodos = [...completeTodos]; newCompleteTodos.splice(index, 1); + const newIncompleteTodos = [...incompleteTodos, completeTodos[index]]; -
配列
newIncompleteTodosを、配列incompleteTodosの更新関数であるsetIncompleteTodosに引数として渡す
=配列incompleteTodosを更新するTodo.jsxconst onClickBack = () => { const newCompleteTodos = [...completeTodos]; newCompleteTodos.splice(index, 1); const newIncompleteTodos = [...incompleteTodos, completeTodos[index]]; + setIncompleteTodos(newIncompleteTodos); -
配列
newIncompleteTodosを、配列completeTodosの更新関数であるsetCompleteTodosに引数として渡す
=配列completeTodosを更新するTodo.jsxconst onClickBack = () => { const newCompleteTodos = [...completeTodos]; newCompleteTodos.splice(index, 1); const newIncompleteTodos = [...incompleteTodos, completeTodos[index]]; setIncompleteTodos(newIncompleteTodos); + setCompleteTodos(newCompleteTodos); };
処理
-
completeTodosに登録されている完了のTODOがリストとして表示されている - ユーザーが
完了ボタンを押下する -
onClickイベントが発火する {completeTodos.map((todo, index) => {...}}によってindexが各li要素に渡されているので、該当の要素がわかる-
() => onClickBack(index)が実行される - 該当の
indexがonClickBackに渡る - 最新の配列
completeTodosをnewCompleteTodosに渡す(コピーする) -
spliceメソッドを使って、newCompleteTodosの第一引数indexから第二引数1番目を削除する - 最新の配列
incompleteTodosをnewIncompleteTodosに渡し(コピーし)、該当のindexを配列completeTodos内の要素を追加する -
newCompleteTodosを更新関数setCompleteTodosに渡す -
completeTodosが更新される - Reactの再レンダリングがトリガーされる
-
newIncompleteTodosを更新関数setIncompleteTodosに渡す -
incompleteTodosが更新される - Reactの再レンダリングがトリガーされる
-
buttonタグ(戻す)を押下した時に、配列completeTodosの任意の要素が削除・出力される

