Recoil使い方(要修正) 個人メモ

# Recoilとは
- グローバルステートを管理するFacebookが開発しているライブラリ
## 使い方
- ```npm install recoil``` でRecoilをインストール
- Reduxでいう```<provider>```のようにして```<RecoilRoot>```で囲む
```react
ReactDOM.render(
  <RecoilRoot>
    <App/>
  </RecoilRoot>,
  document.getElementById('root')
);
```
- ```atom```と```useRecoilState```をインポートする
```react
import { atom, useRecoilState } from "recoil"; 
```
- ```atom関数```を使用して```useRecoilState```に必要なパーツ(引数)を作成
``` react
const countState = atom<number>({ 
  //ユニークキー
  key: "simple/counter", 
  //初期値
  default: 0 
});
```
- ```useRecoilState```にstateを引数として入れる
```react
// 変数と関数を同時生成
const [count, setCount] = useRecoilState<number>(countState)
// 関数のみ生成
const setCount = useSetRecoilState<number>(countState)
// 変数のみ生成
const count = useRecoilValue<number>(countState)
```
- ```setCount```でstateを更新する
```react
<button onClick={() => setCount((c) => c + 1)}>Clicked : {count}</button>
```
## 全体像
```react
import React from 'react'
import { atom, useRecoilState } from 'recoil'

//stateに必要なパーツ(引数)atom関数を使用
const countState = atom<number>({
  //ユニークキー
  key: 'simple/counter',
  //初期値
  default: 0,
})

export const Footer: React.FC = () => {
  // useRecoilStateにぶち込む
  const [count, setCount] = useRecoilState<number>(countState)
  return (
    <>
      <p>Footer</p>
      <button onClick={() => setCount((c) => c + 1)}>Clicked : {count}</button>
    </>
}
```
## Selector関数
- atomのstateを加工することが可能
- 複数のatomのセットが可能(やり方不明)
```react
const reCountState = selector<number>({
  //ユニークキー
  key: 'simple/reCounter',
  //atomで作ったstateを3倍に加工する(getはあくまで値を読み取る用)
  get: ({ get }) => get(countState) * 3,
  //setは値の加工で任意で追加
  set: ({ get, set }, newValue) => {
    if (typeof newValue == 'number') {
      set(countState, newValue * 3)
    }
  },
})
```
- selector関数で作ったstateで変数と関数を生成
```react
//selectorで変数と関数を生成
const [reCount, setCount] = useRecoilState<number>(reCountState)
```
## selector、atom、set関数の処理の順序
① ```atom```で指定してある初期値を取得して表示
② ```selector```を介して生成された```state```の値は、```atom```の値を参照した後```getメソッド```の処理が走った上で表示される
③ set関数で値をセットする
④ ```selector```で設定されている```setメソッド```の処理が走り、```state```が更新される
⑤ ①に戻る
```react
//state生成に必要なatom関数を使用
const countState = atom<number>({
  //ユニークキー
  key: 'simple/counter',
  //初期値
  default: 0,
})

//atomの値を加工するselector関数
const reCountState = selector<number>({
  //ユニークキー
  key: 'simple/reCounter',
  //②　atom関数で生成したstateの変数を3倍に加工する(※1回目の処理後 3 *2回目の処理後　12)
  get: ({ get }) => get(countState) * 3,
  //setの方もgetの順番と同じっぽい
  set: ({ get, set }, newValue) => {
    if (typeof newValue == 'number') {
      set(countState, newValue * 3)
    }
  },
})

export const Footer: React.FC = () => {
  //atom関数で生成したstateから変数のみ生成
  const [count] = useRecoilState<number>(countState)

  //selector関数で生成したstateで変数と関数を生成
  const [reCount, setCount] = useRecoilState<number>(reCountState)

  return (
    <>
      {/*①　set関数で +1 される(※1回目の処理後 1 *2回目の処理後　4) 
         ③　selectorで加工された値がset関数の引数に入る(※1回目の挿入値(初動は加工されない) 0 ※2回目の挿入値 3)*/}
      <button onClick={() => setCount((c) => c + 1)}>atom counter</button>
      <p>atom:{count}</p>
      <p>selector:{reCount}</p>
    </>
  )
}
```
## 動的にAtomを生成する
- atomFamily関数というもので識別子をつけられる
```react
//atomFamily関数でstateを作成(作成したstateは関数として使う)
const itemStateFamily = atomFamily<number, SerializableParam>({
  key: 'atomfamily',
  default: 0,
})

//生成した関数に識別子を渡す
const atom = itemStateFamily("My name")
//上記の変数を使って関数・変数を作成
const [state, setState] = useRecoilState(atom)
```
- atomと違う要素としてdefaultに関数を指定できる
```react
const numStateFamily = atomFamily<number, number>({
  key: 'atomfamily',
  //識別子として渡したものがここに入る？？
  default: (i) => i * 5,
})

//defaultに設定した関数の引数に今回の引数が入る
const numAtom = numStateFamily(numName)
```
## 動的にSelectorを生成する
```react
export const selectorFamilySample = selectorFamily<number, string>({
  key: 'selector-family',
  get:
    (arg) =>
    ({ get }) => {
      return get(itemStateFamily(arg))
    },
  set:
    (arg) =>
    ({ set }, value) => {
      console.log('value')
      console.log(value)
      if (typeof value == 'number') set(itemStateFamily(arg), value * 3)
    },
})
```
## 他コンポーネントからの呼び出し
- ```useRecoilState```に必要な引数であるatom関数やselector関数で作った```state```をexportして外部で使う
- 関連してる```state```は更新されると全て再レンダリングが走る
```react
import { countState } from '../RecoilStart/RecoilStart'

export const StateCall: React.FC = () => {
  const [callState, setCallState] = useRecoilState<number>(countState)

  return (
    <>
      <button onClick={() => setCallState((c) => c + 1)}>
        別コンポーネントで呼び出し
      </button>
      <p>call:{callState}</p>
    </>
  )
}
```
## 参考記事
- https://mizchi.dev/study-recoil
- https://nulab.com/ja/blog/nulab/recoil-example/
- https://sbfl.net/blog/2020/05/17/react-experimental-recoil-usage/