Chapter 09無料公開

Todo(タスク) の仕様を考える (その 2)

Kei Touge
Kei Touge
2021.11.20に更新

前章で見た通り、todos ステート配列をリストとして展開するためには、配列の各要素へその識別子を持たせる必要があります。

配列の各要素、つまり Todo 型のタスクそれぞれに一意な key を持たせる必要が生じたため、Todo 型そのものを拡張しなければなりません。

ここでは、id プロパティとして一意な数字 (number 型) を持たせることにします。
また、一意であるはずの識別子が書き換えられてはならないため、readonly(読み取り専用) のプロパティとします。

src/App.tsx
  type Todo = {
    value: string;
+   readonly id: number;
  };

https://typescript-jp.gitbook.io/deep-dive/type-system/readonly

Todo 型オブジェクトには id プロパティの指定が必須となったため、handleOnSubmit() メソッドを更新しなければいけません。

src/App.tsx
const handleOnSubmit = (
    e: React.FormEvent<HTMLFormElement | HTMLInputElement>
  ) => {
    e.preventDefault();
    if (!text) return;

    const newTodo: Todo = {
      value: text,
      /**
      * Todo 型オブジェクトの型定義が更新されたため、
      * number 型の id プロパティの存在が必須になった
      */
      id: new Date().getTime(),
    };

    setTodos([newTodo, ...todos]);
    setText('');
  };

これでそれぞれのタスクが一意なプロパティを持つようになったので、これを key プロパティへ適用しましょう。
<li></li> タグに key (=id) を付加します。

src/App.tsx
      <ul>
        {todos.map((todo) => {
          return <li key={todo.id}>{todo.value}</li>;
        })}
      </ul>

2021-04-25 10.01.09.png

key は特別なプロパティであり、React によって予約されています。

https://zenn.dev/luvmini511/articles/f7b22d93e9c182
この章のソースコード全文
src/App.tsx
import { useState } from 'react';

type Todo = {
  value: string;
  readonly id: number;
};

export const App = () => {
  const [text, setText] = useState('');
  const [todos, setTodos] = useState<Todo[]>([]);

  const handleOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setText(e.target.value);
  };

  const handleOnSubmit = () => {
    if (!text) return;

    const newTodo: Todo = {
      value: text,
      id: new Date().getTime(),
    };

    setTodos([newTodo, ...todos]);
    setText('');
  };

  return (
    <div>
      <form
        onSubmit={(e) => {
          e.preventDefault();
          handleOnSubmit();
        }}
      >
        <input type="text" value={text} onChange={(e) => handleOnChange(e)} />
        <input type="submit" value="追加" onSubmit={handleOnSubmit} />
      </form>
      <ul>
        {todos.map((todo) => {
          return <li key={todo.id}>{todo.value}</li>;
        })}
      </ul>
    </div>
  );
};