⚙️

Recoilはグローバル状態管理ライブラリなのか

2022/12/19に公開

RecoilをReduxと同じ「グローバル状態管理ライブラリ」とまとめてしまうことに若干違和感があったので、自分なりに考えをまとめてみました。これらは何に対してグローバルなのか考えると、異なる特徴を持ったライブラリに思えるのです。

ReduxとRecoilの登場と背景

まずReduxやRecoilが登場した頃の課題感を、筆者なりにまとめてみます。

Redux

Reduxの歴史は状態管理ライブラリの中では古く、2015年に初版が公開されました。ReduxはReactに依存してないので、他のViewライブラリやRedux単体でも採用可能でしたが、React・Reduxのセットは、フロントエンドに一貫した設計を設けることができるため高い人気を得ました。

筆者の主観ですが、Redux登場当時はReactの実装でバケツリレーをしないこと・複雑な状態を扱えることが多くのエンジニアの関心ごとだったように思えます。当時のReactにはContext APIもなく、Reduxなどを利用しなければバケツリレーするしかありませんでした。Reduxはこれを解決し、複雑化しやすい状態管理を単方向データフローという一貫した設計の元Reactから切り離せるという魅力を持っていました。

Recoil

一方Recoilは2020年に登場したReact専用の状態管理ライブラリです。Meta(当時Facebook)が作成し、現在でもexperimentalながら高い人気を得ています。Reduxと大きく異なる点として、code splittingが効くのでページ単位でのバンドルサイズを減らすことも大きく注目されました。

こちらも筆者の主観ですが、Recoilが登場した頃や昨今のエンジニアの関心ごとは、hooksで扱いやすいかどうかなのではないかと考えられます。Redux登場当時はHOC(higher-order component)でReduxとReactを繋げていましたが、hooks登場以降のReduxはhooksの外でやることが少々多い(actionやreducerの定義、middlewareの導入など)ライブラリとなってしまった印象があります。その点Recoilは、React組み込みのuseStateと同様のAPIを提供してたり、利用者にとっては簡略化されてるように感じます。

その他の状態ライブラリの台頭

また、ReduxとRecoil登場時の異なる点として、react-hook-formswrなど、用途に応じた状態管理ライブラリの台頭が挙げられます。これらを利用することで開発者が明示的に管理する状態が減り、よりシンプルになった結果、状態管理自体に扱いやすさが求められているようになったのではないかと筆者は考えています。

グローバル状態管理とは

このように、ReduxやRecoilは登場時期の違いから解決したかった課題背景が異なると筆者は考えていますが、これらは同じ「グローバル状態管理」ライブラリなのでしょうか?「グローバル」の意味の解像度をもう少し上げてみましょう。

「グローバル状態管理」には、以下2つの意味合いが混在していると筆者は考えています。

  • 参照スコープ: 文字通り参照スコープ。
  • ライフタイムスコープ: 状態の生存期間スコープ。

これらについては以前書いた記事でも述べています。上記定義は下記の記事で行なった定義をより簡略化したものです。

https://zenn.dev/akfm/articles/react-state-scope#ライフタイムによるstateの分類

多くの文脈において「グローバル」とは参照スコープにおけるグローバルを指していることが多いように感じますが、複数箇所で参照しない場合、例えばページ移動でコンポーネントがアンマウントされても保持し続けたい状態なども、ReduxやRecoilで状態管理することがあります。

Reduxは参照スコープとライフタイムスコープの2つの意味においてグローバルですが、Recoilの状態は参照スコープに対しては必ずしもグローバルとは限りません。

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

function TodoList() {
  const todoList = useRecoilValue(todoListState);

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

上記はRecoilのサンプルを少し修正した例です。上記のtodoListStateは、複数のコンポーネントでの利用が想定されない状態のため、exportしていません。しかしuseStateとは異なり、コンポーネントのアンマウント後もtodoListStateの値は保持され、再マウントすると元の値が返ってきます。

これはライフタイムスコープ的にはグローバルな状態であることを示しますが、参照スコープにおいてはexportしてないので他ファイルで利用されることがありません。

ライブラリ 参照スコープ ライフタイム
Redux グローバル グローバル
Recoil 必ずしもグローバルではない グローバル

Recoilはグローバル状態管理ライブラリと呼ぶべきなのか

このことから、Recoilは参照スコープ的には必ずしも「グローバル」ではありません。Recoilのドキュメントから言葉を借りると、Recoilはshared state、つまり共有状態管理ライブラリと呼ぶ方が適切かもしれません。

一方Reduxは同じく共有状態をグローバルな参照スコープによって解決しているので、共有状態管理ライブラリでも間違いはないですが、やはりその特性からグローバル状態管理ライブラリと呼ぶ方がより適切なような気がします。

まとめ

本稿ではReduxをグローバル状態管理ライブラリ、Recoilを共有状態管理ライブラリと位置付けました。これらのライブラリ選定で悩んでいるなら、これらの特性の違いを考慮した上で選定することをお勧めします。

この記事がReduxやRecoilの選定に役立てば幸いです。

Discussion