React Todoアプリ

概要: このチュートリアルでは、React Todoアプリをステップバイステップで構築する方法を学びます。

React Todoアプリの概要

React Todoアプリでは、新しいTodo項目の追加、既存のTodo項目の編集、Todo項目の削除、Todo項目の完了済みとして設定を行うことができます。

次の図は、これから構築するアプリを示しています。

React Todo App

タイトルを入力してEnter(またはReturn)キーを押すと、新しいTodo項目が作成され、リストに追加されます。

編集ボタンをクリックすると、Todo項目を変更できるフォームが表示されます。また、削除ボタンをクリックすると、リストからTodo項目が削除されます。

Todoをダブルクリックすると、アプリはそれを完了済みとしてマークし、Todoタイトルに打ち消し線を適用します。

コンポーネント階層

Todoアプリのコンポーネント階層を以下に示します。

React todo app - component hierarchy
  • 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のタイトルを更新します。

変更が完了した後、TodoEditTodoShowコンポーネントを表示する必要があります。

新しいReact Todoアプリの作成

ステップ1. ターミナルを開き、次のコマンドを実行して新しいReactアプリを作成します

npx create-react-app todoCode language: JavaScript (javascript)

ステップ2. srcディレクトリ内のすべてのファイルを削除します。

ステップ3。プロジェクトディレクトリの下に、次のcomponentsディレクトリを作成します。すべてのReactコンポーネントをcomponentsディレクトリに格納します。

ステップ4。次の新しいファイルを作成します

ファイルディレクトリ説明
index.jssrcReactアプリのエントリファイル
App.jssrcAppコンポーネント
App.csssrcCSSファイル
TodoCreate.jssrc/componentsTodoCreateコンポーネント
TodoList.jssrc/componentsTodoListコンポーネント
TodoShow.jssrc/componentsTodoShowコンポーネント
TodoEdit.jssrc/componentsTodoEditコンポーネント

チュートリアルの最後にあるプロジェクトソースコードのアイコン、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コンポーネントを使用します。

ステップ7TodoCreateコンポーネントに次のコードを追加します

const TodoCreate = () => {
  return <div>Todo Create</div>;
};

export default TodoCreate;Code language: JavaScript (javascript)

ステップ7TodoListコンポーネントに次のコードを追加します

const TodoList = () => {
  return <div>Todo List</div>;
};

export default TodoList;Code language: JavaScript (javascript)

ステップ8TodoEditコンポーネントに次のコードを追加します

const TodoEdit = () => {
  return <div>Todo Edit</div>;
};

export default TodoEdit;Code language: JavaScript (javascript)

ステップ9TodoShowコンポーネントに次のコードを追加します

const TodoShow = () => {
  return <div>Todo Show </div>;
};

export default TodoShow;Code language: JavaScript (javascript)

ステップ10。ターミナルで次のコマンドを実行してアプリを実行します

npm startCode language: JavaScript (javascript)

アプリは次のようになります

React Todo App Initial

Appコンポーネント

ステップ1Appコンポーネントの状態として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()関数では、

まず、idtitle、および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アプリのソースコードをダウンロードしてください。

このチュートリアルは役に立ちましたか?