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

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

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

# 目次
1. [インストール](#インストール)
2. [セットアップ](#セットアップ)
3. [Atom](#Atom)
4. [Selector](#Selector)
5. [まとめ](#まとめ)
6. [参考](#参考)

<!-- 各チャプター -->
# インストール

[Installation](https://recoiljs.org/docs/introduction/installation)を参考に各パッケージマネージャーに対応したコマンドを入力してインストール

## 注意

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

## ESLint

eslint-plugin-react-hooksを使用している場合、[ドキュメント](https://recoiljs.org/docs/introduction/installation#eslint)通りに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あたりについて記載します。

# 参考
- [Recoil](https://recoiljs.org/)