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開発でどのように使われるかという雰囲気を掴むための簡単な内容でした。

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

▼以下では、口コミをもとに厳選した「React学習におすすめのUdemy講座3選」をご紹介しています。ぜひ参考にしてみてください!

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

参考文献
ABOUT ME
テルプロ
東京都在住のアプリエンジニア。大学では、ソフトウェア開発の研究に取り組む。長期のエンジニアインターンシップを経て、実務スキルを磨き、現在はフリーランスエンジニアとしても活動中。メインはモバイルアプリ開発。IT関連の記事監修も行い、技術の共有と普及に励んでいます。 監修実績(レバテックフリーランス
Flutter関連の書籍を出版しました!