【Recoil&React】グローバルstateの管理

### ゴール
Recoilを知る
使い方理解する

### はじめに
この記事はすべて〇〇である

### ざっと調べたstate管理方法
- Recoil
    - 今回の注目ポイント
    - 理由：簡単そう。シンプル。使いやすそう。
- React Context
    - 標準的な方法
    - ページ間情報のやり取りや何層にもコンポーネントが分かれているときのstate管理に限界を感じた
- Redux
    - 一番良く聞く管理方法
    - しっかりした方法ではあるが、手続きが面倒で軽量なアプリには向かなそう。大人数開発とかでは強そう
- SWR
    - Next.jsを見ていて発見
    - ただRecoilの方が流行りそうな気がしたため今回は深い追いせず

### Recoil（リコイル？）
https://recoiljs.org/
Recoilは、Meta社(旧Facebook)が開発し、2020年5月にリリースされた状態管理ライブラリ
Context APIのデメリットとReduxの使いづらさを解消するために開発されたらしい

大きく「Atom」「Selector」と呼ばれる２つの概念があります。

##### Recoilのインストール
`yarn add recoil` または、 `npm install recoil`

### Atom（最低限の使い方）

##### RecoilRootで囲う
- Recoilを使用するためにはコンポーネントをRecoilRootで囲う

```_app.tsx
import '../styles/globals.css'
import { RecoilRoot } from "recoil"; //追加

const MyApp = ({ Component, pageProps }) => {
  return (
    <RecoilRoot>
      <Component {...pageProps} />
    </RecoilRoot>
  );
}

export default MyApp;
```

##### Atomの作成

- keyにアプリケーション全体で一意なキー、defaultに状態管理したい値の初期値を設定します。
- 状態管理したい値一つごとにAtomを一つ作っていく。
- アプリケーションで一つのstateを作れるようだが、一つずつ作るほうが良さそうらしい。
- 個人的には同じような用途なら同じファイルで良さそうな気がした。
      煩雑にならない程度の軽量な作りの方がメリット活かせそう。
 
``` atoms/demoAtoms.tsx
import { atom } from "recoil";

export const textState = atom<string>({
    key: "demo/textState",
    default: ""
});

export const testTextState = atom<string>({
    key: "demo/test/textState",
    default: ""
});

export const titleListState = atom({
  key: "demo/titleListState",
  default: [
    {
      id: 0,
      title: "タイトル",
    },
  ],
});

interface UserStateIf {
  id: string | null;
  name: string;
  email: string;
}

export const userState = atom<UserStateIf>({
  key: "demo/userState",
  default: {
    id: null,
    name: "",
    email: ""
  }
});

```

##### Atomの状態の取得・更新

```
import { useRecoilState } from 'recoil'
import { textState } from '../atoms/demoAtoms'

export const TextInput = () => {
  // useStateみたいに使う
  const [text, setText] = useRecoilState<string>(textState)
  
  省略...
}
```

##### メリット
- 下層コンポーネントにpropsで渡さなくて済む（複数階層の場合、不必要なコンポーネントにも渡す必要が出てくるケースがある）
- 「useRecoilState」を使うと「RecoilRoot」で囲まれた要素は、全て再レンダリングが走るようだが、
「useRecoilValue」、「useSetRecoilState」というものを使うと全てが再レンダリングされることを防ぐことができるらしい
※stateの値を読み取るコンポーネントは、stateが更新されると自動的にレンダリングされます。
```
import { useRecoilValue, useSetRecoilState } from 'recoil'
import { textState } from '../atoms/demoAtoms'

export const TextInput = () => {
  // 読み取り専用
  const count = useRecoilValue<string>(textState)
  // 更新専用
  const setCount = useSetRecoilState<string>(textState)
  
  省略...
}
```


### Selector
SelectorはAtomにもとづく派生state。
Atomの値を何らかの計算や加工した結果を返す。
※関数が渡せたりするようだが、個人的に活用できる用途が想像できていない。


### 値の永続化（persist）
上記でだけでは、リロードするとstateがリセットされるので、何かしらの方法で永続化させる必要がある。

##### Recoilのインストール

`npm install recoil-persist`

```
import { atom } from "recoil";
import { recoilPersist } from "recoil-persist"; //追加

const { persistAtom } = recoilPersist(); //追加

export const userState = atom({
    key: "user",
    default: "",
    effects_UNSTABLE: [persistAtom] //追加
});
```

この書き方だと、デフォはローカルストレージに保存されます
セッションストレージに保存した方が良さそうなので

```
const { persistAtom } = recoilPersist();
↓ に変更してみた
const { persistAtom } = recoilPersist({
	key: "recoil-persist",
	storage: typeof window === "undefined" ? undefined : sessionStorage
});

```
参考：https://sunday-morning.app/posts/2021-11-10-change-recoil-persist-storage-with-nextjs
参考：https://qiita.com/terufumi1122/items/76bafb9eed7cfc77b798
参考：

### 感想
- 簡易アプリやスピード重視のアプリ開発、Poc開発ならシンプルで学習コスト（useSateに似てるため）も低く楽なのでRecoil良さそう
  大規模やこれからしっかり成長させていくアプリならReduxも良いとは思う。