React

React + TypeScript でTodoリストを作る!サンプルを用いて解説

こんにちは、アプリ開発者のテルです!

「React + TypeScript の基本的な使い方を知りたい」とお悩みではないでしょうか?

テル

本記事ではそんな悩みを解決していきます!

本記事を読むメリット
  1. Todoリストの作り方をサンプルで理解できる
  2. コードを公開しているので、自分の環境で確かめることができる

React + TypeScript Todoリストの作り方

事前準備

パッケージをインストール

今回使用するパッケージは特にありません。

プロジェクト構成

今回実装するアプリの「リポジトリ構成」と「完成イメージ」は以下の通りです。

リポジトリ構成

完成イメージ

今回実装するTodoリストは、上記の通りです。TypeScriptがReact開発でどのように使われるかという雰囲気を掴むための簡単な内容になっています。

TODOの「追加・更新・削除」といった基本的な機能が実装されています。

では早速、こちらのアプリを作っていきましょう!

ソースコード

index.tsx

import React from "react";
import ReactDOM from "react-dom";
import App from "./App";

ReactDOM.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
  document.getElementById('root')
);

App.tsx

import Todo from "./components/Todo";

const App = () => {
  return (
    <>
      <h2>- Todo List -</h2>
      <Todo />
    </>
  );
};

export default App;

components/Todo.tsx

import { useState } from "react";
import List from "./List";
import Form from "./Form";

// TODOの型を定義する
export type Todo = {
  id: number;
  content: string;
  editing: boolean;
};

const Todo = () => {

 // デフォルトのTODOリスト
  const todosList = [
    {
      id: 1,
      content: "店を予約する",
      editing: false,
    },
    {
      id: 2,
      content: "卵を買う",
      editing: false,
    },
    {
      id: 3,
      content: "郵便を出す",
      editing: false,
    },
  ];

 // TODOリストのState管理
  const [todos, setTodos] = useState<Todo[]>(todosList);

 // 削除
  const deleteTodo = (id: number) => {
    const newTodos = todos.filter((todo) => {
      return todo.id !== id;
    });
    setTodos(newTodos);
  };

 // 作成
  const createTodo = (todo: Todo) => {
    setTodos([...todos, todo]);
  };

 // 更新
  const updateTodo = (todo: Todo) => {
    const newTodos = todos.map((_todo) => {
      return _todo.id === todo.id ? { ..._todo, ...todo } : { ..._todo };
    });
    setTodos(newTodos);
  };

  return (
    <>
      // 各々に必要なPropsを渡す
      <List todos={todos} deleteTodo={deleteTodo} updateTodo={updateTodo} />
      <Form createTodo={createTodo} />
    </>
  );
};
export default Todo;

components/List.tsx

import { Item } from "./Item";
import { Todo } from "./Todo";

// 受け取るPropsの型を定義する
type Listprops = {
  todos: Todo[];
  deleteTodo: Function;
  updateTodo: Function;
};

const List: React.FC<Listprops> = ({ todos, deleteTodo, updateTodo }) => {
 // 押したTODOのidを[deleteTodo]に渡す
  const complete = (id: number) => {
    deleteTodo(id);
  };
  return (
    <div>
  // TODOリストを取得して[map関数]で1つ1つ取り出す
      {todos.map((todo) => {
        return (
   // [Item]に必要なPropsを渡す
          <Item
            key={todo.id}
            todo={todo}
            complete={complete}
            updateTodo={updateTodo}
          />
        );
      })}
    </div>
  );
};

export default List;

components/Item.tsx

import React, { useState } from "react";
import { Todo } from "./Todo";

// 受け取るPropsの型を定義する
type ItemProps = {
  todo: Todo;
  complete: Function;
  updateTodo: Function;
};

export const Item: React.FC<ItemProps> = ({ todo, complete, updateTodo }) => {
 // テキストのState管理
  const [editingContent, setEditingContent] = useState(todo.content);

  // 編集状態の切り替え
  const editMode = () => {
    const newTodo = {
      ...todo,
      editing: !todo.editing,
    };
    updateTodo(newTodo);
  };

 // 編集を確定する
  const confirmContent = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const newTodo = {
      ...todo,
      content: editingContent,
      editing: !todo.editing,
    };
    updateTodo(newTodo);
  };

  return (
    <div key={todo.id}>
      <form onSubmit={confirmContent} style={{ display: "inline" }}>
        <span onDoubleClick={editMode}>
          {todo.editing ? (
            <input
              type="text"
              placeholder="入力してね"
              value={editingContent}
              onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
                setEditingContent(e.target.value)
              }
            />
          ) : (
            todo.content
          )}
        </span>
      </form>
      <button onClick={() => complete(todo.id)}>-</button>
    </div>
  );
};

components/Form.tsx

import React, { useState } from "react";

// 受け取るPropsの型を定義する
type FormProps = {
  createTodo: Function;
};

const Form: React.FC<FormProps> = ({ createTodo }) => {
 // 入力値のState管理
  const [enteredTodo, setEnteredTodo] = useState("");

 // 作成したTODOを[createTodo]に渡す
  const addTodo = (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const newTodo = {
      id: Math.floor(Math.random() * 1e5),
      content: enteredTodo,
    };
    createTodo(newTodo);
    setEnteredTodo("");
  };

  return (
    <div>
      <form onSubmit={addTodo}>
        <input
          type="text"
          placeholder="入力してね"
          value={enteredTodo}
          onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
            setEnteredTodo(e.target.value)
          }
        />
        <button>+</button>
      </form>
    </div>
  );
};

export default Form;

大変お疲れ様でした!以上でアプリは完成です!

▼Todoリストを作成することで学べること

  1. コンポーネント分割の方法
  2. コンポーネント間でのstateの受け渡し方
  3. イベントリスナーの使い方
  4. map/filterメソッドの使い方

Todoリストの作成は、React開発の基礎の部分を押さえられるので初心者の方には非常におすすめです。上記のコードを参考に、ぜひ色々と試してみてください!

▼JS版もあるので興味がある方はぜひご覧ください。

まとめ

今回は「React + TypeScript の基本的な使い方」を解説しました。

今回解説したTodoリストは、TypeScriptがReact開発でどのように使われるかという雰囲気を掴むための簡単な内容でした。

TypeScriptの実践的なスキルを身につけたいという方にはUdemyがおすすめです。オンラインコースの数は10万以上。世界で4000万人以上の人が学習に利用しています。

▶︎【2022年最新】TypeScript学習におすすめのUdemy講座3選

最後までご覧いただきありがとうございました。ではまた!

参考文献
Flutter関連の書籍を出版しました!