RecoilでReactの子孫要素がatomの値が取得できない時の対処法

RecoilでReactの子孫要素がatomの値が取得できない時の対処法
目次

React の 状態管理ライブラリの Recoil を使ってしばらくしたのでTipsを書いておきます。 使いこなせているかと言われると自信はありませんが備忘録として。

環境情報

  • react 16.14.x
  • recoil 0.1.2

Recoilで子孫要素からatomの値が取得できない

Recoil を使って中規模程度のアプリケーションを実装していたところ、特定の子孫要素からatomの値が取得できない、という事象に遭遇しました。 もちろん App.tsx のようなアプリケーションの最上位のコンポーネントに近い箇所で <RecoilRoot> を呼び出しています。

React Contextをぶった切ってしまう実装にはRecoilBridgeを使う

実は、Render Treeの中にcontext stateを正しく渡さないコンポーネントが存在する場合、該当するコンポーネントの前後でcontext stateをブリッジをする必要があります。 例えば、react-three-fiber<Canvas> タグの子要素ではcontextをブリッジする必要があります。

ブリッジする方法として、Recoilでは useRecoilBridgeAcrossReactRoots_UNSTABLE が提供されています。ただし、UNSTABLE と名前に付与されている通りなので、使用する場面はご自身で判断してください。

以下のサンプルコードでは <SomeChildComponent> 以降のコンポーネントでRecoilのatomの値を参照させることができます。

 1const SomeComponent: React.FC = () => {
 2
 3  const RecoilBridge = useRecoilBridgeAcrossReactRoots_UNSTABLE();
 4
 5  return (
 6    <Canvas
 7      camera={{ fov: 50, aspect: 4.0 / 3.0, near: 0.4, far: 1.0 }}
 8    >
 9      <RecoilBridge>
10        <SomeChildComponent />
11      </RecoilBridge>
12    </Canvas>
13  )
14}

状態管理のアプローチ自体が異なりますが、 React.createContext で自前で定義したReact Contextを使った場合では通常以下のようにブリッジする必要があるので、 RecoilBridge の方が見た目的にもすっきりすることがわかります。

 1const SomeComponent: React.FC = () => {
 2
 3  return (
 4    <SomeContext.Consumer>
 5      {(value) => (
 6        <Canvas
 7          camera={{ fov: 50, aspect: 4.0 / 3.0, near: 0.4, far: 1.0 }}
 8        >
 9          <SomeContext.Provider value={value}>
10            <SomeChildComponent />
11          </SomeContext.Provider>
12        </Canvas>
13      )}
14    </SomeContext.Consumer>
15  )
16}

まとめ

  • Recoilを使ったアプリケーションで子コンポーネントがatomを参照できない場合には、context stateを渡していないコンポーネントが存在する可能性がある
  • context stateを渡していないコンポーネントの中で useRecoilBridgeAcrossReactRoots_UNSTABLE を使うことで参照可能になる
  • ただし APIが UNSTABLE なので注意しましょう