2
0

More than 1 year has passed since last update.

Recoilを使用した状態管理(Atom / Selector)

Last updated at Posted at 2022-12-24

はじめに

状態管理の採用で、jotai・Zustand・useReducer・Recoilなど技術選定していて調査していたので、公式のドキュメントを読みながらメモ。

Recoil自体はfacebookexperimental/Recoilなので、早くメジャーリリースをしてほしいですね。

目次

  1. インストール
  2. セットアップ
  3. Atom
  4. Selector
  5. まとめ
  6. 参考

インストール

Installationを参考に各パッケージマネージャーに対応したコマンドを入力してインストール

注意

RecoilビルドはES2015に変換されず、ES2015での使用はサポートされていないっぽい。
レガシーな環境をサポートすることは少ないが、使用に関しては念頭に置いておく必要がある。

ESLint

eslint-plugin-react-hooksを使用している場合、ドキュメント通りにconfigを修正。
また、additionalHooksのリストに「useRecoilCallback」を追加することでuseRecoilCallbackに渡された依存関係が正しくない場合に、warningとして警告されるようになる。

セットアップ

RecoilRootを設置

ルートコンポーネントにReacoilRootを設定。

import type { NextPage } from "next";
import type { AppProps } from "next/app";
import { RecoilRoot } from "recoil";

const App: NextPage<AppProps> = ({ Component, pageProps }) => {
  return (
    <RecoilRoot>
      <Component {...pageProps} />
    </RecoilRoot>
  );
};

export default App;


Atom

  • key: atomに一意のkeyを与える(この文字列はアプリケーション全体でユニークである必要がある)
  • default: default値
  • effects: Atom Effectsのオプション配列
  • dangerouslyAllowMutability: イミュータブルなオブジェクトの変更を許可するかどうかのフラグ
import { atom } from 'recoil';

export const counter = atom({
  key: 'myCounter',
  default: 0,
});
  • 定義したAtomへ書き込みと読み取りを双方行う場合は、useRecoilStateフックを使用する
import { FC } from 'react';
import { useRecoilState } from 'recoil';
import { counter } from '@/stores/counter'

type Props = {};

export const Counter: FC<Props> = () => {
  const [count, setCount] = useRecoilState(counter);
  const incrementByOne = () => setCount((count) => count + 1);

  return (
    <div>
      Count: {count}
      <br />
      <button onClick={incrementByOne}>Increment</button>
    </div>
  );
}

Selector

  • key: selectorに一意のkeyを与える(この文字列はアプリケーション全体でユニークである必要がある)
  • get: ゲッターを定義、PromiseやLoadable、また別のAtomやSelectorを返すことが可能
  • set: セッターを定義
  • dangerouslyAllowMutability: イミュータブルなオブジェクトの変更を許可するかどうかのフラグ
  • cachePolicy_UNSTABLE: 内部セレクターキャッシュの動作を定義、現在はevictionのみらしい。
const proxySelector = selector({
  key: 'ProxySelector',
  get: ({get}) => ({...get(myAtom), extraField: 'hi'}),
  set: ({set}, newValue) => set(myAtom, newValue),
});
import { FC, Suspense } from 'react';
import { selector, useRecoilValue } from 'recoil';

const myQuery = selector({
  key: 'MyDBQuery',
  get: async () => {
    const response = await fetch('データ取得用のAPI');
    return response.json();
  },
});

type QueryResultsProps = {};

const QueryResults: FC<QueryResultsProps> = () => {
  const queryResults = useRecoilValue(myQuery);

  return (
    <div>
      {queryResults.foo}
    </div>
  );
};

type ResultsSectionProps = {};

const ResultsSection: FC<ResultsSectionProps> = () => {
  return (
    <Suspense fallback={<div>Loading...</div>}>
      <QueryResults />
    </Suspense>
  );
};

まとめ

AtomとSelectorについて記載。
selectorが非同期処理と組み合わせることができて、Suspenseと親和性が高い設計になっているので使いやすいと感じた(SuspenceはPromiseをthrowするとfallbackコンテンツを表示するが、selector側でもそのような実装になっているため)
次回はAtomFamily、selectorFamily、Loadableあたりについて記載します。

参考

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0