⛓️

Recoilを使ってみる。

2022/06/06に公開

Recoilとは

RecoilはMeta社が開発している、
Reactとの互換性が高いステートマネージングフレームワークになります。
グローバルステートを管理する事ができます。

https://recoiljs.org/

主な機能

atom

Storeとしての役割
実際に共通データとして保持しておく場所になります。
文字列や配列、オブジェクトなどを格納しておく事ができます。
Recoilでは基本的にkeyは一つのatomと一対一になる必要があるので、
こちらのkeyを一意にしておく必要があります。

const todoListState = atom({
  key: 'TodoList',
  default: [],
});

上記で言うと、todoを格納していく配列になります。
こちらに保存されてる情報がコンポーネントを跨いで、各所から読み込む事ができます。

selector

取得のGetとしての役割
少し変わっているところとして、getプロパティなどで、getを引数として受け取るというところです。
setプロパティも同じように、getやsetを引数として受け取ることができます。

selectorは、atomのデータを取得するのに整形した形で返すために使います。
複数のatomからデータを取得する事ができます。
上記に合わせて、以下の形で取得します。

const todoListFilterState = atom({
  key: 'TodoListFilter',
  default: 'Show All',
});
const filteredTodoListState = selector({
  key: 'FilteredTodoList',
  get: ({get}) => {
    const filter = get(todoListFilterState);
    const list = get(todoListState);

    switch (filter) {
      case 'Show Completed':
        return list.filter((item) => item.isComplete);
      case 'Show Uncompleted':
        return list.filter((item) => !item.isComplete);
      default:
        return list;
    }
  },
});

上記では、todoListFilterStateの値によって、取得できるtodoが異なるように
取得できるものを変更させる事ができます。

この他にも、情報を付与して取得するようにも取得できます。

const getPrice = selector({
  key: 'getPrice',
  get: ({ get }) => {
    const price = get(priceState)
    return `${price}`
  }
})

selectorにはsetパラメータもあります。
これは新しい値を受け取ることで、setで新しい値を適応させることができます。

const tempFahrenheit = atom({
  key: 'tempFahrenheit',
  default: 32,
});

const tempFahrenheit = atom({
  key: 'tempFahrenheit',
  default: 32,
});

const tempCelsius = selector({
  key: 'tempCelsius',
  get: ({get}) => ((get(tempFahrenheit) - 32) * 5) / 9,
  set: ({set}, newValue) => set(tempFahrenheit, (newValue * 9) / 5 + 32),
});

// 使用
const [tempF, setTempF] = useRecoilState(tempFahrenheit);
const [tempC, setTempC] = useRecoilState(tempCelsius);

const addTenCelsius = () => setTempC(tempC + 10);
const addTenFahrenheit = () => setTempF(tempF + 10);

ここでは、tempC + 10tempF + 10がnewValueに入ってくることで、
新しい値に変換してくれている感じです。
setプロパティの中でもgetを使うことができるので、別のatomを注入することも可能です。

family

atomFamilyとselectorFamilyがあります。
パラメータを受け取り、その返り値を決める事ができます。
atomFamilyは、パラメータを受け取り、そのパラメータによってデフォルト値を変えることができますが、基本的にはatomと変わらない形で使えます。

const myAtomFamily = atomFamily({
  key: ‘MyAtom’,
  default: param => defaultBasedOnParam(param),
});

selectorFamilyは、atomFamilyと同様にパラメータを受け取ることができます。
他のatomに依存した値をデフォルトに指定する時に使います。
この時、selectorFamilyに対してのkeyも指定する必要がある点に注意が必要です。

const myAtomFamily = atomFamily({
  key: ‘MyAtom’,
  default: selectorFamily({
    key: 'MyAtom/Default',
    get: param => ({get}) => {
      const otherAtomValue = get(otherState);
      return computeDefaultUsingParam(otherAtomValue, param);
    },
  }),
});

感想

個人的に良さそうなstate管理フレームワークだと思いました。
特に複雑なことを考えずに、呼び出し元のコンポーネントのみのレンダリングになるという点や
複数のatom stateへのアクセスの便利性は現在あるようなstate管理より簡略的に書けたりするのでスッキリとしたコードになるかなという印象を持ちました。

ただ、どのような設計にするかは結構分かれると思うので、プロダクトに対する設計の仕方は少し難しいような気もしました。
まだプロダクトで使用した訳ではないので、これから使っていく中で分かったことがあれば、追記できたらいいなと思います。

参考

https://zenn.dev/yuta_ura/articles/react-context-api

https://engineering.linecorp.com/ja/blog/line-sec-frontend-using-recoil-to-get-a-safe-and-comfortable-state-management/

https://nulab.com/ja/blog/nulab/recoil-example/

Discussion