lamechang-dev

Webフロントエンドエンジニア lamechangのブログ。

【TypeScript】【React】【Recoil】翻訳しながらそれなりに型をつけて細々と進めていくRecoil Tutorial その1 Atom編

f:id:lamechang_dev:20220213113950p:plain

本記事は、Reactの提供元であるFacebook改めMetaが開発中の新しい状態管理ライブラリである Recoil の公式チュートリアルを翻訳しつつ、TypeScriptで型付けをしながら進めていく連載の第一弾です。細々とやっていきます🙄 まずは Atom について。

recoiljs.org

Atoms

Atoms contain the source of truth for our application state. In our todo-list, the source of truth will be an array of objects, with each object representing a todo item.

We'll call our list atom todoListState and create it using the atom() function:


Atomsは私たちのアプリケーションの状態のための信頼すべきソースを含みます。私たちのTODOリストでは、信頼すべきソースはオブジェクトの配列になり、各オブジェクトはTodo項目を表します。

atom() 関数を利用して、リストのAtom todoListStateを作成しましょう。

少し後で明らかになりますが、このTodoオブジェクトの型が分かるのでちゃんとジェネリクスを使ってAtomの型を明らかにしてあげましょう。

type Todo = {
  id: number;
  text: string;
  isComplete: boolean;
};

const todoListState = atom<Array<Todo>>({
  key: "todoListState",
  default: [],
});

 

We give our atom a unique key and set the default value to an empty array. To read the contents of this atom, we can use the useRecoilValue() hook in our TodoList component:

The commented-out components will be implemented in the sections that follow.


アトムに一意のキーを与え、デフォルト値を空の配列に設定します。このアトムの内容を読み取るには、TodoListコンポーネントでuseRecoilValue()フックを使用できます。

コメントアウトされたコンポーネントは、以下のセクションで実装されます。

実際の例では function() 記法で書かれていますが、あまり主流でないのでArrow Functionで書いてあげていいと思います。 ちなみに、先ほど todoListStateに型をつけたので、以下のtodoList には Todo[] の型がついていることがIDEから確認できるはずです。

const TodoList: React.VFC = () => {
  const todoList = useRecoilValue(todoListState);

  return (
    <>
      <TodoItemCreator />
      {todoList.map((todoItem) => (
        <TodoItem key={todoItem.id} item={todoItem} />
      ))}
    </>
  );
};

 

To create new todo items, we need to access a setter function that will update the contents of the todoListState. We can use the useSetRecoilState() hook to get a setter function in our TodoItemCreator component:


新しいToDoアイテムを作成するには、ToDoListateの内容を更新するセッター関数にアクセスする必要があります。 ToDoItemCreatorコンポーネントでsetter機能を取得するには、useSetRecoilState() フックを使用できます。

以下のコードのsetTodoListの型は SetterOrUpdater<Todo[]> というものになり、SetterOrUpdaterの型は以下の通りです。現在の値を受け取るCallbackも渡せるし、純粋に値を渡すこともできるということですね。useState()にそっくりですね😮

export type SetterOrUpdater<T> = (valOrUpdater: ((currVal: T) => T) | T) => void;`
const TodoItemCreator: React.VFC = () => {
  const [inputValue, setInputValue] = useState("");
  // setTodoList: SetterOrUpdater<Todo[]> 
  const setTodoList = useSetRecoilState(todoListState);

  const addItem = () => {
    setTodoList((oldTodoList) => [
      ...oldTodoList,
      {
        id: getId(),
        text: inputValue,
        isComplete: false,
      },
    ]);
    setInputValue("");
  };

  const onChange = ({ target: { value } }: { target: { value: string } }) => {
    setInputValue(value);
  };

  return (
    <div>
      <input type="text" value={inputValue} onChange={onChange} />
      <button onClick={addItem}>Add</button>
    </div>
  );
};

// utility for creating unique Id
let id = 0;
function getId() {
  return id++;
}

 

Notice we use the updater form of the setter function so that we can create a new todo list based on the old todo list.

The TodoItem component will display the value of the todo item while allowing you to change its text and delete the item. We use useRecoilState() to read todoListState and to get a setter function that we use to update the item text, mark it as completed, and delete it:


注意:古いTODOリストに基づいて新しいToDoリストを作成できるように、セッター機能のアップデータ形式を使用します。

TodoItemコンポーネントはTODO項目の値を表示し、テキストを変更してアイテムを削除することを可能にします。 userecoilState()を使用してTodolistStateを読むことと、アイテムテキストの更新(テキストの更新・完了のマーク・または削除)に使用するセッター関数を取得します。

const TodoItem: React.VFC<{ item: Todo }> = ({ item }) => {
  const [todoList, setTodoList] = useRecoilState(todoListState);
  const index = todoList.findIndex((listItem) => listItem === item);

  const editItemText = ({
    target: { value },
  }: {
    target: { value: string };
  }) => {
    const newList = replaceItemAtIndex(todoList, index, {
      ...item,
      text: value,
    });

    setTodoList(newList);
  };

  const toggleItemCompletion = () => {
    const newList = replaceItemAtIndex(todoList, index, {
      ...item,
      isComplete: !item.isComplete,
    });

    setTodoList(newList);
  };

  const deleteItem = () => {
    const newList = removeItemAtIndex(todoList, index);

    setTodoList(newList);
  };

  return (
    <div>
      <input type="text" value={item.text} onChange={editItemText} />
      <input
        type="checkbox"
        checked={item.isComplete}
        onChange={toggleItemCompletion}
      />
      <button onClick={deleteItem}>X</button>
    </div>
  );
};

const replaceItemAtIndex = (arr: Todo[], index: number, newValue: Todo) => {
  return [...arr.slice(0, index), newValue, ...arr.slice(index + 1)];
};

const removeItemAtIndex = (arr: Todo[], index: number) => {
  return [...arr.slice(0, index), ...arr.slice(index + 1)];
};

 

And with that we've got a fully functional todo list! In the next section we'll see how we can use selectors to take our list to the next level.


これで、完全に機能するToDoリストができました。次のセクションでは、セレクターを使用してリストを次のレベルに引き上げる方法を説明します。

と、いうことでここまでが完了すると以下のようなシンプルなTodoリストが出来上がります。次は Selectorの部分について書きます。

f:id:lamechang_dev:20220205121634p:plain