概要: このチュートリアルでは、React Todoアプリをステップバイステップで構築する方法を学びます。
React Todoアプリの概要
React Todoアプリでは、新しいTodo項目の追加、既存のTodo項目の編集、Todo項目の削除、Todo項目の完了済みとして設定を行うことができます。
次の図は、これから構築するアプリを示しています。

タイトルを入力してEnter(またはReturn)キーを押すと、新しいTodo項目が作成され、リストに追加されます。
編集ボタンをクリックすると、Todo項目を変更できるフォームが表示されます。また、削除ボタンをクリックすると、リストからTodo項目が削除されます。
Todoをダブルクリックすると、アプリはそれを完了済みとしてマークし、Todoタイトルに打ち消し線を適用します。
コンポーネント階層
Todoアプリのコンポーネント階層を以下に示します。
TodoCreate
– 新しいTodo項目をリストに追加します。TodoList
– Todoリストを表示します。TodoShow
– 個々のTodo項目を表示します。TodoEdit
– Todo項目を編集します。
Appは、Todoリストであるアプリケーションの状態を保持します。
TodoCreateコンポーネント
TodoCreate
コンポーネントは、新しいTodo項目をリストに追加します。これは、Appコンポーネントから渡されたcreateTodo
関数を実行することで行います。
createTodo
関数は、AppコンポーネントのTodoリストに新しいTodo項目を追加します。
TodoListコンポーネント
TodoList
コンポーネントは、Todoリストを表示します。Appコンポーネントから渡されたTodoリストを使用して、TodoShow
コンポーネントのリストをレンダリングします。
TodoShowコンポーネント
TodoShow
コンポーネントは、各Todo項目をレンダリングします。
Todo項目のタイトルをダブルクリックすると、完了と未完了の状態が切り替わります。TodoShow
は、App
コンポーネントからTodoList
コンポーネントを介して渡されたchangeTodo
関数を実行することで、Todo項目の状態を変更します。
TodoShow
コンポーネントの削除ボタンをクリックすると、リストからTodo項目が削除されます。これは、AppコンポーネントからTodoList
コンポーネントを介して渡されたremoveTodo
コールバックを実行することで行います。
編集ボタンをクリックすると、TodoEdit
コンポーネントが表示されます。
TodoEditコンポーネント
TodoEdit
コンポーネントは、App
コンポーネントからTodoList
およびTodoShow
コンポーネントを介して渡されたchangeTodo
関数を実行することで、Todoのタイトルを更新します。
変更が完了した後、TodoEdit
はTodoShow
コンポーネントを表示する必要があります。
新しいReact Todoアプリの作成
ステップ1. ターミナルを開き、次のコマンドを実行して新しいReactアプリを作成します
npx create-react-app todo
Code language: JavaScript (javascript)
ステップ2. src
ディレクトリ内のすべてのファイルを削除します。
ステップ3。プロジェクトディレクトリの下に、次のcomponents
ディレクトリを作成します。すべてのReactコンポーネントをcomponents
ディレクトリに格納します。
ステップ4。次の新しいファイルを作成します
ファイル | ディレクトリ | 説明 |
---|---|---|
index.js | src | Reactアプリのエントリファイル |
App.js | src | App コンポーネント |
App.css | src | CSSファイル |
TodoCreate.js | src/components | TodoCreate コンポーネント |
TodoList.js | src/components | TodoList コンポーネント |
TodoShow.js | src/components | TodoShow コンポーネント |
TodoEdit.js | src/components | TodoEdit コンポーネント |
チュートリアルの最後にあるプロジェクトソースコードのアイコン、SVGアイコンファイル、およびApp.css
ファイルを使用してください。
ステップ5. index.js
ファイルに次のコードを追加します
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const el = document.querySelector('#root');
const root = ReactDOM.createRoot(el);
root.render(<App />);
Code language: JavaScript (javascript)
index.js
ファイルは、画面にAppコンポーネントをレンダリングします。
ステップ6. App.js
ファイルに次のコードを追加します
import TodoList from './components/TodoList';
import TodoCreate from './components/TodoCreate';
import './App.css';
const App = () => {
return (
<main className="main">
<h1>
React Todo <span>Streamline Your Day, the React Way!</span>
</h1>
<TodoList />
<TodoCreate />
</main>
);
};
export default App;
Code language: JavaScript (javascript)
App
コンポーネントは、TodoList
およびTodoCreate
コンポーネントを使用します。
ステップ7。TodoCreate
コンポーネントに次のコードを追加します
const TodoCreate = () => {
return <div>Todo Create</div>;
};
export default TodoCreate;
Code language: JavaScript (javascript)
ステップ7。TodoList
コンポーネントに次のコードを追加します
const TodoList = () => {
return <div>Todo List</div>;
};
export default TodoList;
Code language: JavaScript (javascript)
ステップ8。TodoEdit
コンポーネントに次のコードを追加します
const TodoEdit = () => {
return <div>Todo Edit</div>;
};
export default TodoEdit;
Code language: JavaScript (javascript)
ステップ9。TodoShow
コンポーネントに次のコードを追加します
const TodoShow = () => {
return <div>Todo Show </div>;
};
export default TodoShow;
Code language: JavaScript (javascript)
ステップ10。ターミナルで次のコマンドを実行してアプリを実行します
npm start
Code language: JavaScript (javascript)
アプリは次のようになります

Appコンポーネント
ステップ1。App
コンポーネントの状態としてtodos
配列を定義します
const [todos, setTodos] = useState([]);
Code language: JavaScript (javascript)
デフォルトでは、todos
状態は空です。
ステップ2。新しいTodo項目をtodosリストに追加する関数を定義します
const createTodo = (title) => {
const newTodo = { id: crypto.randomUUID(), title: title, completed: false };
const updatedTodos = [...todos, newTodo];
setTodos(updatedTodos);
};
Code language: JavaScript (javascript)
createTodo()
関数では、
まず、id
、title
、およびcompleted
の3つのプロパティを持つ新しいTodo項目オブジェクトを作成します。
id
は、Webブラウザが提供するメソッドcrypto.randomUUID()
から返されたUUID
値を取ります。 completed
プロパティの値は、デフォルトでfalse
です。
次に、新しく作成されたTodo項目を含む新しい配列を作成します
const updatedTodos = [...todos, newTodo];
Code language: JavaScript (javascript)
push()
メソッドを使用して新しいTodo項目をtodos
配列に追加する場合、todos
配列の項目のみが変更され、todos
配列の参照は同じであることに注意してください。したがって、状態が変更されないため、Reactはコンポーネントを再レンダリングしません。
3番目に、setTodos()
関数を使用してtodos
配列を新しい配列で更新します
setTodos(updatedTodos);
Code language: JavaScript (javascript)
この関数呼び出しにより、コンポーネントが再レンダリングされます。
ステップ3。 IDで指定されたTodo項目をTodoリストから削除するremoveTodo()
関数を定義します
const removeTodo = (id) => {
const updatedTodos = todos.filter((todo) => todo.id !== id);
setTodos(updatedTodos);
};
Code language: JavaScript (javascript)
removeTodo()
関数では、
まず、filter()配列メソッドを使用して、指定されたIDを持つTodoを含まない新しい配列を返します
const updatedTodos = todos.filter((todo) => todo.id !== id);
Code language: JavaScript (javascript)
次に、新しい配列でtodos
状態を更新します
setTodos(updatedTodos);
Code language: JavaScript (javascript)
ステップ4. IDで指定されたtodo
オブジェクトのtitle
およびcompleted
プロパティを変更するchangeTodo
関数を定義します
const changeTodo = (id, title, completed = false) => {
const updatedTodos = todos.map((todo) => {
if (todo.id === id) {
return { ...todo, title, completed };
}
return todo;
});
setTodos(updatedTodos);
};
Code language: JavaScript (javascript)
ステップ5。 todos配列、removeTodo
関数、およびchangeTodo
関数をTodoList
コンポーネントにpropsとして渡し、createTodo
関数をTodoCreate
コンポーネントのpropとして渡します
<TodoList todos={todos} removeTodo={removeTodo} changeTodo={changeTodo} />
<TodoCreate createTodo={createTodo} />
Code language: JavaScript (javascript)
完全なApp
コンポーネントは次のとおりです
import TodoList from './components/TodoList';
import TodoCreate from './components/TodoCreate';
import './App.css';
import { useState } from 'react';
const App = () => {
const [todos, setTodos] = useState([]);
const createTodo = (title) => {
const newTodo = { id: crypto.randomUUID(), title, completed: false };
const updatedTodos = [...todos, newTodo];
setTodos(updatedTodos);
};
const removeTodo = (id) => {
const updatedTodos = todos.filter((todo) => todo.id !== id);
setTodos(updatedTodos);
};
const changeTodo = (id, title, completed = false) => {
const updatedTodos = todos.map((todo) => {
if (todo.id === id) {
return { ...todo, title, completed };
}
return todo;
});
setTodos(updatedTodos);
};
return (
<main className="main">
<h1>
React Todo <span>Streamline Your Day, the React Way!</span>
</h1>
<TodoList todos={todos} removeTodo={removeTodo} changeTodo={changeTodo} />
<TodoCreate createTodo={createTodo} />
</main>
);
};
export default App;
Code language: JavaScript (javascript)
TodoCreateコンポーネント
TodoCreate
コンポーネントを変更します。
ステップ1. createTodo
関数をTodoCreate
コンポーネントの引数として追加します
const TodoCreate = ({ createTodo }) => {
// ...
}
Code language: JavaScript (javascript)
ステップ2. Todoのタイトルを入力するための入力フィールドを含むフォームを返します
return (
<form className="todo-create">
<input
type="text"
name="title"
id="title"
placeholder="Enter a todo"
/>
</form>
);
Code language: JavaScript (javascript)
ステップ3. 入力フィールドのタイトルを保持する状態を定義し、その値を空の文字列に初期化します
const [title, setTitle] = useState('');
Code language: JavaScript (javascript)
ステップ4. title状態を入力フィールドの現在の値に更新する関数handleChange
を定義します
const handleChange = (e) => {
setTitle(e.target.value);
};
Code language: JavaScript (javascript)
ステップ5. title状態とhandleChange
イベントハンドラを入力フィールドに使用します
<input
type="text"
name="title"
id="title"
placeholder="Enter a todo"
value={title}
onChange={handleChange}
/>;
Code language: JavaScript (javascript)
ステップ6. フォームが送信されたら実行される関数handleSubmit
を定義します
const handleSubmit = (e) => {
e.preventDefault();
createTodo(title);
setTitle('');
};
Code language: JavaScript (javascript)
handleSubmit()
関数では、
まず、イベントオブジェクトのpreventDefault()
メソッドを呼び出して、ページの再読み込みを防ぎます
e.preventDefault();
Code language: JavaScript (javascript)
次に、createTodo
関数を呼び出して新しいTodo項目を作成し、Appコンポーネントのtodos
状態に追加します。
3番目に、title状態を空の文字列に設定することにより、入力フィールドを空白にリセットします
setTitle('');
Code language: JavaScript (javascript)
完全なTodoCreate
コンポーネントは次のとおりです
import { useState } from 'react';
const TodoCreate = ({ createTodo }) => {
const [title, setTitle] = useState('');
const handleChange = (e) => {
setTitle(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
createTodo(title);
setTitle('');
};
return (
<form onSubmit={handleSubmit} className="todo-create">
<input
type="text"
name="title"
id="title"
placeholder="Enter a todo"
value={title}
onChange={handleChange}
/>
</form>
);
};
export default TodoCreate;
Code language: JavaScript (javascript)
Todoを入力してEnterキーを押すと、新しいTodo項目が作成され、Todoリストに追加されます。ただし、TodoList
コンポーネントを変更するまで表示されません。
TodoCreateコンポーネント
ステップ1. todos、removeTodo
関数、およびchangeTodo
関数をTodoList
コンポーネントのpropsとして追加します
const TodoList = ({ todos, removeTodo, changeTodo }) => {
// ...
}
Code language: JavaScript (javascript)
ステップ2. todos配列に基づいてTodoShow
コンポーネントのリストを生成します。また、todoオブジェクト、removeTodo
関数、およびchangeTodo
関数をTodoShow
コンポーネントに渡します
const renderedTodos = todos.map((todo) => {
return (
<TodoShow
key={todo.id}
todo={todo}
removeTodo={removeTodo}
changeTodo={changeTodo}
/>
);
});
Code language: JavaScript (javascript)
ステップ3. Todoリストをレンダリングします
return <ul className="todo-list">{renderedTodos}</ul>;
Code language: JavaScript (javascript)
完全なTodoList
コンポーネントは次のとおりです
import TodoShow from './TodoShow';
const TodoList = ({ todos, removeTodo, changeTodo }) => {
const renderedTodos = todos.map((todo) => {
return (
<TodoShow
key={todo.id}
todo={todo}
removeTodo={removeTodo}
changeTodo={changeTodo}
/>
);
});
return <ul className="todo-list">{renderedTodos}</ul>;
};
export default TodoList;
Code language: JavaScript (javascript)
TodoShowコンポーネント
ステップ1. todoオブジェクト、removeTodo
関数、およびchangeTodo
関数をTodoShow
コンポーネントのpropsとして追加します
const TodoShow = ({ todo, removeTodo, changeTodo }) => {
// ..
}
Code language: JavaScript (javascript)
ステップ2. trueの場合にTodoEdit
コンポーネントを表示するShowEdit
状態を定義します
const [showEdit, setShowEdit] = useState(false);
Code language: JavaScript (javascript)
デフォルトでは、TodoEdit
コンポーネントは非表示になっています。
ステップ3. ユーザーが削除ボタンをクリックしたときに実行されるイベントハンドラhandleDelete
を定義します
const handleDelete = (e) => {
removeTodo(todo.id);
};
Code language: JavaScript (javascript)
ステップ4. ユーザーが編集ボタンをクリックしたときに実行されるイベントハンドラhandleEdit
を定義します
const handleEdit = (e) => {
setShowEdit(true);
};
Code language: JavaScript (javascript)
handleEdit
は、showEdit
状態をtrueに設定して、TodoEdit
コンポーネントを表示します。
ステップ5. ユーザーがTodo項目をダブルクリックしたときに実行されるイベントハンドラhandleDoubleClick
を削除します
const handleDoubleClick = (e) => {
changeTodo(todo.id, todo.title, !todo.completed);
};
Code language: JavaScript (javascript)
handleDoubleClick
は、changeTodo
関数を呼び出して、completedプロパティの値を切り替えます。
ステップ6. TodoEdit
コンポーネントのフォームが送信されたときに実行されるイベントハンドラhandleSubmit
を定義します
const handleSubmit = (id, title) => {
changeTodo(id, title);
setShowEdit(false);
};
Code language: JavaScript (javascript)
仕組み。
まず、changeTodo
関数を呼び出して、IDに基づいてTodoのタイトルを変更します
changeTodo(id, title);
Code language: JavaScript (javascript)
次に、showEdit
状態をfalseに設定することにより、TodoEdit
コンポーネントを非表示にします
setShowEdit(false);
Code language: JavaScript (javascript)
ステップ7. showEdit
がtrueの場合、TodoEdit
コンポーネントをレンダリングします
if (showEdit) {
return (
<li className="todo">
<TodoEdit todo={todo} onSubmit={handleSubmit} />
</li>
);
}
Code language: JavaScript (javascript)
このコードでは、todoオブジェクトとhandleSubmit
関数をTodoEdit
コンポーネントに渡します。
ステップ8. Todo項目をレンダリングします
return (
<li className="todo" onDoubleClick={handleDoubleClick}>
<p className={todo.completed && 'completed'}>{todo.title}</p>
<div className="actions">
<button onClick={handleDelete}>
<img src={DeleteIcon} title="Delete" />
</button>
<button onClick={handleEdit}>
<img src={EditIcon} title="Edit" />
</button>
</div>
</li>
);
Code language: JavaScript (javascript)
仕組み。
まず、handleDoubleClick
イベントハンドラをli要素のonDoubleClick
イベントに追加します。
<li className="todo" onDoubleClick={handleDoubleClick}>
Code language: JavaScript (javascript)
次に、todo
オブジェクトのcompleted
プロパティがtrueの場合、completed
CSSクラスを<p>要素に追加します。
<p className={todo.completed && 'completed'}>{todo.title}</p>
Code language: JavaScript (javascript)
3番目に、対応するボタンのonClick
イベントにhandleDelete
およびhandleEdit
イベントハンドラーを追加します。
<div className="actions">
<button onClick={handleDelete}>
<img src={DeleteIcon} title="Delete" />
</button>
<button onClick={handleEdit}>
<img src={EditIcon} title="Edit" />
</button>
</div>;
Code language: JavaScript (javascript)
TodoEditコンポーネント
まず、todoオブジェクトとonSubmit
関数をTodoEdit
コンポーネントのプロップとして追加します。
const TodoEdit = ({ todo, onSubmit }) => {
// ...
};
Code language: JavaScript (javascript)
ステップ2. TodoEdit
コンポーネントのtitle状態を定義し、その値をtodoオブジェクトのtitleに初期化します。
const [title, setTitle] = useState(todo.title);
Code language: JavaScript (javascript)
ステップ3. ユーザーが編集フォームを介してタイトルを変更したときに実行されるイベントハンドラーを定義します。
const handleChange = (e) => {
setTitle(e.target.value);
};
Code language: JavaScript (javascript)
handleChange
関数は、setTitle
関数を呼び出して、title状態を入力要素の現在の値に更新します。
ステップ4. ユーザーがフォームを送信したときに実行されるイベントハンドラーを定義します。
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(todo.id, title);
};
Code language: JavaScript (javascript)
まず、イベントオブジェクトのpreventDefault()
メソッドを呼び出して、ページの再読み込みを防ぎます。
e.preventDefault();
Code language: JavaScript (javascript)
次に、todoのtitle
を更新し、TodoEdit
コンポーネントを非表示にするonSubmit
関数を呼び出します。
onSubmit(todo.id, title);
Code language: JavaScript (javascript)
ステップ5. フォームをレンダリングし、イベントハンドラーを接続します。
return (
<form className="todo-edit">
<input type="text" value={title} onChange={handleChange} />
<button type="submit" onClick={handleSubmit}>
<img src={CheckIcon} title="Save" />
</button>
</form>
);
Code language: JavaScript (javascript)
TodoEdit
コンポーネントの完全なコードは次のとおりです。
import { useState } from 'react';
import CheckIcon from '../check.svg';
const TodoEdit = ({ todo, onSubmit }) => {
const [title, setTitle] = useState(todo.title);
const handleChange = (e) => {
setTitle(e.target.value);
};
const handleSubmit = (e) => {
e.preventDefault();
onSubmit(todo.id, title);
};
return (
<form className="todo-edit">
<input type="text" value={title} onChange={handleChange} />
<button type="submit" onClick={handleSubmit}>
<img src={CheckIcon} title="Save" />
</button>
</form>
);
};
export default TodoEdit;
Code language: JavaScript (javascript)
React Todoアプリのソースコードをダウンロードしてください。