実施条件
[React][L6] リストの項目をボタンで削除・出力する2を理解していること
環境
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/
│ ├── components/
│ │ ├── InputTodo.jsx
│ │ ├── IncompleteTodos.jsx
│ │ └── CompleteTodos.jsx
│ ├── 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);
};
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, index) => {
return (
<li>
<div className="list-row">
<p>{todo}</p>
<button onClick={() => onClickBack(index)}>戻す</button>
</div>
</li>
);
})}
</ul>
</div>
</>
);
};
Todo.jsx
import { useState } from "react";
+import { CompleteTodos } from "./components/CompleteTodos";
+import { IncompleteTodos } from "./components/IncompleteTodos";
+import { InputTodo } from "./components/InputTodo";
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="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>
+ <IncompleteTodos
+ todos={incompleteTodos}
+ onClickComplete={onClickComplete}
+ onClickDelete={onClickDelete}
+ />
<div className="complete-area">
<p className="title">完了のTODO</p>
<ul>
{completeTodos.map((todo, index) => {
return (
<li>
<div className="list-row">
<p>{todo}</p>
<button onClick={() => onClickBack(index)}>戻す</button>
</div>
</li>
);
})}
</ul>
</div>
</>
);
};
解説
目的
-
return
以下の各div
タグ(input-area
incomplete-area
complete-area
)をコンポーネント化する
コーディング
InputTodo.jsx
のコンポーネント化
-
components
ディレクトリと各コンポーネントを書くフォルダを作成するfolda_structure├── package.json ├── public/ │ └── index.html ├── src/ +│ ├── components/ +│ │ └── InputTodo.jsx │ ├── index.jsx │ ├── Todo.jsx │ └── styles.css
-
コンポーネントの雛形を用意する
InputTodo.jsxexport const InputTodo = () => { return ( ); };
-
コンポーネント化したい機能を、元のファイルから切り取る
Todo.jsxreturn ( <> - <div className="input-area"> - <input - placeholder="TODOを入力" - value={todoText} - onChange={onChangeTodoText} - ></input> - <button onClick={onClickAdd}>追加</button> - </div>
InputTodo.jsxexport const InputTodo = () => { return ( + <div className="input-area"> + <input + placeholder="TODOを入力" + value={todoText} + onChange={onChangeTodoText} + ></input> + <button onClick={onClickAdd}>追加</button> + </div> ); };
-
todoText
(配列)onChangeTodoText
(関数)onClickAdd
(関数)がないためエラーとなる -
上記の配列や関数を
InputTodo.jsx
がProps
を受け取れる状態にするInputTodo.jsx-export const InputTodo = () => { +export const InputTodo = (props) => { return ( <div className="input-area"> <input placeholder="TODOを入力" value={todoText} onChange={onChangeTodoText} ></input> <button onClick={onClickAdd}>追加</button> </div> ); };
-
Todo.jsx
がProps
を送れる状態にするTodo.jsximport { useState } from "react"; +import { InputTodo } from "./components/InputTodo"; import "./styles.css"; //省略// return ( <> + <InputTodo + />
-
Todo.jsx
が送るProps
を設定するTodo.jsximport { useState } from "react"; import { InputTodo } from "./components/InputTodo"; import "./styles.css"; //省略// return ( <> <InputTodo + todoText={todoText} + onChange={onChangeTodoText} + onClick={onClickAdd} />
-
InputTodo.jsx
が受け取るProps
を設定するInputTodo.jsxexport const InputTodo = (props) => { + const { todoText, onChange, onClick } = prorps; return ( <div className="input-area"> <input placeholder="TODOを入力" value={todoText} onChange={onChangeTodoText} ></input> <button onClick={onClickAdd}>追加</button> </div> ); };
-
受け取る
Props
に合わせて変数名を修正するInputTodo.jsxexport const InputTodo = (props) => { + const { todoText, onChange, onClick } = prorps; return ( <div className="input-area"> <input placeholder="TODOを入力" value={todoText} - onChange={onChangeTodoText} + onChange={onChange} ></input> - <button onClick={onClickAdd}>追加</button> + <button onClick={onClick}>追加</button> </div> ); };
IncompleteTodos
のコンポーネント化
-
components
ディレクトリと各コンポーネントを書くフォルダを作成するfolda_structure├── package.json ├── public/ │ └── index.html ├── src/ │ ├── components/ │ │ ├── InputTodo.jsx +│ │ └── IncompleteTodos.jsx │ ├── index.jsx │ ├── Todo.jsx │ └── styles.css
-
コンポーネントの雛形を用意する
IncompleteTodos.jsxexport const IncompleteTodos = () => { return ( ); };
-
コンポーネント化したい機能を、元のファイルから切り取る
Todo.jsxreturn ( //省略// - <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>
IncompleteTodos.jsxexport const IncompleteTodos = () => { return ( + <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> ); };
-
incompleteTodos
(配列)onClickComplete
(関数)onClickDelete
(関数)がないためエラーとなる -
上記の配列や関数を
IncompleteTodos.jsx
がProps
を受け取れる状態にするIncompleteTodos.jsx-export const IncompleteTodos = () => { +export const IncompleteTodos = (props) => { return ( <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> ); };
-
Todo.jsx
がProps
を送れる状態にするTodo.jsximport { useState } from "react"; import { InputTodo } from "./components/InputTodo"; +import { IncompleteTodos } from "./components/InCompleteTodos"; import "./styles.css"; //省略// return ( //省略// + <IncompleteTodos + />
-
Todo.jsx
が送るProps
を設定するTodo.jsximport { useState } from "react"; import { InputTodo } from "./components/InputTodo"; +import { IncompleteTodos } from "./components/InCompleteTodos"; import "./styles.css"; //省略// return ( //省略// <IncompleteTodos + todos={incompleteTodos} + onClickComplete={onClickComplete} + onClickDelete={onClickDelete} />
-
IncompleteTodos.jsx
が受け取るProps
を設定するIncompleteTodos.jsxexport const IncompleteTodos = (props) => { + const { todos, onClickComplete, onClickDelete } = props; return ( <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> ); };
-
受け取る
Props
に合わせて変数名を修正するIncompleteTodos.jsxexport const IncompleteTodos = (props) => { + const { todos, onClickComplete, onClickDelete } = props; return ( <div className="incomplete-area"> <p className="title">未完了のTODO</p> <ul> - {incompleteTodos.map((todo, index) => { + {todos.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> ); };
CompleteTodos
のコンポーネント化
-
components
ディレクトリと各コンポーネントを書くフォルダを作成するfolda_structure├── package.json ├── public/ │ └── index.html ├── src/ │ ├── components/ │ │ ├── InputTodo.jsx │ │ ├── IncompleteTodos.jsx +│ │ └── CompleteTodos.jsx │ ├── index.jsx │ ├── Todo.jsx │ └── styles.css
- コンポーネントの雛形を用意する
CompleteTodos.jsx
export const CompleteTodos = () => { return ( ); };
- コンポーネント化したい機能を、元のファイルから切り取る
return (
//省略//
- <div className="complete-area">
- <p className="title">完了のTODO</p>
- <ul>
- {completeTodos.map((todo, index) => {
- return (
- <li>
- <div className="list-row">
- <p>{todo}</p>
- <button onClick={() => onClickBack(index)}>戻す</button>
- </div>
- </li>
- );
- })}
- </ul>
- </div>
export const CompleteTodos = () => {
return (
+ <div className="complete-area">
+ <p className="title">完了のTODO</p>
+ <ul>
+ {completeTodos.map((todo, index) => {
+ return (
+ <li>
+ <div className="list-row">
+ <p>{todo}</p>
+ <button onClick={() => onClickBack(index)}>戻す</button>
+ </div>
+ </li>
+ );
+ })}
+ </ul>
+ </div>
);
};
-
completeTodos
(配列)onClickBack
(関数)がないためエラーとなる - 上記の配列や関数を
CompleteTodos.jsx
がProps
を受け取れる状態にする
-export const CompleteTodos = () => {
+export const CompleteTodos = (props) => {
return (
<div className="complete-area">
<p className="title">完了のTODO</p>
<ul>
{completeTodos.map((todo, index) => {
return (
<li>
<div className="list-row">
<p>{todo}</p>
<button onClick={() => onClickBack(index)}>戻す</button>
</div>
</li>
);
})}
</ul>
</div>
);
};
-
Todo.jsx
がProps
を送れる状態にする
import { useState } from "react";
import { InputTodo } from "./components/InputTodo";
+import { CompleteTodos } from "./components/CompleteTodos";
import { IncompleteTodos } from "./components2/IncompleteTodos";
//省略//
return (
//省略//
+ <CompleteTodos
+ />
-
Todo.jsx
が送るProps
を設定する
import { useState } from "react";
import { InputTodo } from "./components/InputTodo";
+import { CompleteTodos } from "./components/CompleteTodos";
import { IncompleteTodos } from "./components2/IncompleteTodos";
//省略//
return (
//省略//
<CompleteTodos
+ todos={completeTodos}
+ onClickBack={onClickBack} />
/>
-
CompleteTodos.jsx
が受け取るProps
を設定する
export const CompleteTodos = (props) => {
+ const { todos, onClickBack } = props;
return (
<div className="complete-area">
<p className="title">完了のTODO</p>
<ul>
{completeTodos.map((todo, index) => {
return (
<li>
<div className="list-row">
<p>{todo}</p>
<button onClick={() => onClickBack(index)}>戻す</button>
</div>
</li>
);
})}
</ul>
</div>
);
};
- 受け取る
Props
に合わせて変数名を修正する
export const CompleteTodos = (props) => {
const { todos, onClickBack } = props;
return (
<div className="complete-area">
<p className="title">完了のTODO</p>
<ul>
- {completeTodos.map((todo, index) => {
+ {todos.map((todo, index) => {
return (
<li>
<div className="list-row">
<p>{todo}</p>
<button onClick={() => onClickBack(index)}>戻す</button>
</div>
</li>
);
})}
</ul>
</div>
);
};