React HookのuseStateを使ってできるだけシンプルなReactのTodoアプリケーションの作成を行います。

React useState ToDoアプリ
React useState ToDoアプリ

環境の構築

任意のディレクトリでnpx crate-react-appを実行してReactのプロジェクトの開発環境を構築します。


 $ npx create-react-app my-app

実行が環境したらmy-appディレクトリに移動してnpm startコマンドを実行します。


 $ cd my-app
 $ npm start 

npm startコマンドが完了したら、ブラウザが自動で起動しReactのトップページが表示されれば環境構築は完了です。

ToDoアプリケーションの作成

作成の準備

Reactインストールディレクトリの下にあるsrcディレクトリの下にあるApp.jsファイルを下記のように変更を行います。TodoのアプリケーションのコードはToDoListファイルの中に記述するためToDoListファイルをimportしています。


import React from 'react';
import TodoList from './components/TodoList';

function App() {
  return (
    <div style={{ margin: "2em"}}>
      <TodoList/>
    </div>
  );
}

export default App;

TodoList.jsファイルの作成

TodoList.jsファイルはsrcディレクトリの下にcomponentsディレクトリを作成してその下に保存します。

作成開始時のToDoList.jsファイルには下記を記述します。


import React, { useState } from 'react';

function TodoList() {

    const initialState = [
        {
            task: 'Learn vue.js',
            isCompleted: false
        },
        {
            task: 'Learn React Hook',
            isCompleted: false
        },
        {
            task: 'Learn Gatsby.js',
            isCompleted: false
        },     
    ]

    const [todos, setTodo] = useState(initialState);

    return (
        <div>
            <h1>ToDo List</h1>
        </div>
  );
}

export default TodoList;

Todoリストの元になる配列initialStateには、3つのタスクがオブジェクトで保存されています。useStateにinitialStateを指定することでtodosにはTodoリストが保存されます。todosの更新にはsetTodoを利用して行います。

ブラウザで確認すると画面にはToDo Listと表示されます。

ToDoリストの開始画面
ToDoリストの開始画面

ToDoリストの表示

ToDoリストを画面上に表示するためにTodoリストが保存されているtodosをmap関数で展開します。


<div>
    <h1>ToDo List</h1>
    <ul>
        { todos.map((todo, index) => (
        <li key={ index }>{ todo.task }</li>
        ))}
    </ul>
</div>

ブラウザで確認を行うとtodosに保存されている3つのタスクがリスト表示されます。

タスクのリスト表示
タスクのリスト表示

inputタグの設定とuseStateの追加

新しいタスクを追加するためのinputタグをリストの上に追加します。


<h1>ToDo List</h1>
Add Task : <input placeholder="Add New Task" />
<ul>
    { todos.map((todo, index) => (
    <li key={ index }>{ todo.task }</li>
    ))}
</ul>

ブラウザで確認すると入力枠が表示されます。

入力枠表示
入力枠表示

入力した情報を保持するための新しい変数taskをuseStateで追加します。


const [task, setTask] = useState('')

inputタグに入力した値を取得するためにonChangeイベントを設定し、value属性の値に追加したtaskを指定します。onChangeイベントで実行する関数名は、handleNewTaskとします。


Add Task : <input value={ task } placeholder="Add New Task" onChange={handleNewTask}/>

onChangeイベントを設定すると文字を入力するたびにイベントが発行されます。

イベント時に実行されるhandleNewTask関数を追加します。inputで入力した値はevent.target.valueで取得することができるので、取得できているかconsole.logを利用して確認します。


const handleNewTask = (event) => {
    console.log(event.target.value)
}

文字を入力する度にコンソールログに入力した文字が表示されれば正常に動作しているので、console.logからsetTaskに変更を行います。


const handleNewTask = (event) => {
    setTask( event.target.value )
}

上記のようにsetTaskに変更すると文字を入力すると入力欄に入力した文字が表示されます。

input欄に入力文字が表示
input欄に入力文字が表示

Formの設定

入力したタスクをtodoリストに保存するためにFormタグを追加する必要があります。FormタグにはonSubmitイベントを追加します。onSubmitイベントで実行する関数名は、handleSubmitとします。onSubmitイベントをEnterを押すと発行されます。


<form onSubmit={ handleSubmit }>
Add Task : <input value={ task } placeholder="Add New Task" onChange={handleNewTask}/>
</form>

handleSubmit関数を追加します。


const handleSubmit = (event) => {
    event.preventDefault()
    if(task === '') return
    setTodo(todos => [...todos,{ task, isCompleted: false}])
    setTask('')
}

event.preventDefault()で通常の動作を停止させています。

handleSubmit関数の中では、useStateで追加した2つの変数のtaskとtodosさらにSetTodoとSetTaskを利用します。

taskには入力欄で入力した文字列が入っているはずなので文字列に何も値がない場合は処理が終わります。文字列が入っている場合は、setTodoによりtodosリストに新しいタスクを追加します。setTaskでは入力欄に入力した文字はTodoリストに追加されるのでその文字を入力欄から削除しています。

下記の部分が既存のTodoリストに入力欄で入力した値を追加している処理です。spread operatorを利用しています。


todos => [...todos,{ task, isCompleted: false}]

ブラウザ上で入力欄に入力した文字がTodoリストに追加されます。追加する際はEnterボタンを押してください。下記では追加により4つのタスクになっています。空白のままEnterを押しても何も起こりません。

リストに新しいタスクを追加
リストに新しいタスクを追加

タスクの削除機能を追加

タスクの追加が行えたので次はタスクの削除機能を追加します。

liタグにspanタグで囲んでXを追加します。


<li key={ index }>{ todo.task } <span>X</span></li>

タスクの右にXが表示されます。

削除のためにXを追加
削除のためにXを追加

このXをクリックするとタスクが削除されるように設定を行なっていきます。spanタグにonClickイベントを追加し、関数をhandleRemoveTaskとします。


<li key={ index }>{ todo.task } <span onClick={ () => handleRemoveTask(index) }>X</span></li>

handleRemoveTask関数を追加します。handleRemoveTaskでは削除するタスクを識別するためにindexを渡します。


const handleRemoveTask = index => {
    const newTodos = [...todos]
    newTodos.splice(index,1)
    setTodo(newTodos)
}

handleRemoveTaskの中では現在のTodoリストをspread operatorを利用して新しい配列newTodosに保存します。spliceメソッドを利用して配列のindex番目の要素を1つ削除しています。削除後はsetTodoで新しいTodoリストで既存のTodoリストを書き換えています。

設定後、Xをクリックするとタスクが削除することができます。3つのタスクが削除になり2つになっています。すべてのタスクを削除することも可能です。

タスクを削除
タスクを削除

ここまでの処理でReact HookのuseStateを利用して、タスクを追加、削除するToDoリストが完成しました。完成時の動作は下記の通りです。

React useState ToDoアプリ
React useState ToDoアプリ

作成したコード全体は下記となります。


import React, { useState } from 'react';

function TodoList() {

    const initialState = [
        {
            task: 'Learn vue.js',
            isCompleted: false
        },
        {
            task: 'Learn React Hook',
            isCompleted: false
        },
        {
            task: 'Learn Gatsby.js',
            isCompleted: false
        },     
    ]

    const [todos, setTodo] = useState(initialState);

    const [task, setTask] = useState('')

    const handleNewTask = (event) => {
        setTask( event.target.value)
    }

    const handleSubmit = (event) => {
        event.preventDefault()
        if(task === '') return
        setTodo(todos => [...todos,{ task, isCompleted: false}])
        setTask('')
    }

    const handleRemoveTask = index => {
        const newTodos = [...todos]
        newTodos.splice(index,1)
        setTodo(newTodos)
    }

    return (
        <div>
            <h1>ToDo List</h1>
            <form onSubmit={ handleSubmit }>
            Add Task : <input value={ task } placeholder="Add New Task" onChange={handleNewTask}/>
            </form>
            <ul>
                { todos.map((todo, index) => (
                <li key={ index }>{ todo.task } <span onClick={ () => handleRemoveTask(index) }>X</span></li>
                ))}
            </ul>
        </div>
  );
}

export default TodoList;