useContextが不便だったのでrecoilで置き換えた

#はじめに
現在React x TypeScript x styled-components x laravel でポートフォリオを作成しています。しかし、ポートフォリオが作成開始時の予想以上にたくさんの状態が必要になり、途中でrecoilで置き換えました。recoilの良い点悪い点を簡単にまとめていきたいと思います。

#なぜ最初にuseContextを選んだのか
Reduxが日本ではいまだに数は多いと思いますが、なにかと複雑でわかりにくく、新規で採用するところは少なくなってきているようです。そのため、Reduxが必要な現場になった時に勉強することにし、とりあえずuseContextを用いて状態管理をしました。しかし、ある理由のため非常にストレスを感じたためrecoilでの状態管理に変更することにしました。

#useContextの使い方
useContextでは状態ごとにcontextを作成し、状態を共有したいコンポーネントたちをproviderで包むことが必要です。**この状態ごとにcontextを作成し、providerで包むという作業がとても憂鬱でした。**

```react:AuthProvider.tsx
import React, { createContext, useContext, useState, VFC } from "react";

const AuthContext = createContext(
    {} as {
        isLogin: boolean;
        setIsLogin: React.Dispatch<React.SetStateAction<boolean>>;
    }
);

type propsType = {
    children: React.ReactElement;
};

export const AuthProvider: VFC<propsType> = (props) => {
    const { children } = props;
    const [isLogin, setIsLogin] = useState<boolean>(false);
    return (
        <AuthContext.Provider value={{ isLogin, setIsLogin }}>
            {children}
        </AuthContext.Provider>
    );
};

export const useAuthContext = () => {
    return useContext(AuthContext);
};

```
これが積み重なっていくとproviderも大変なことになります。
![Screenshot from 2021-10-21 17-55-30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1545070/8e9047c1-0788-8d20-2f92-14b4dc4e3946.png)
多すぎてyoutubeの動画再生ボタンみたいになってます。

これを解決するためにrecoilを導入しました。

#recoilの使い方
recoilはfacebookが開発している状態管理ライブラリであり、typescriptとも相性がよく、シンプルで使いやすいhooks APIが特徴です。

recoilの基本的な使い方はシンプルです
1 RecoilRootコンポーネントで状態を共有したいコンポーネントを包む

```react
export const App: VFC = () => {
    return (
        <RecoilRoot>
            <StylesProvider injectFirst>
                <MuiThemeProvider theme={theme}>
                    <ComponentRouter />
                </MuiThemeProvider>
            </StylesProvider>
        </RecoilRoot>
    );
};
```

2 atom を用いて状態を定義

```react
export const taskListsState = atom<taskListType[]>({
    key: "taskListsState",
    default: [],
});
```
3 recoilのhooksの引数に定義した状態を渡すことで呼び出す

```react
export const Home: VFC = () => {
    const taskLists = useRecoilValue(taskListsState);
    ・・・
    return (
        <>
          ・・・
        </>
    );
};
```
たったこれだけです。recoilへの移行もそれほど時間がかかりませんでした。selectorなどもっといろいろありますが、詳しく知りたい方は公式ドキュメントを見てください。

また、状態を入手できるhooksは大きく分けて３つあります。それぞれのhooksは返り値が違います。

**useRecoilState:値と更新関数どちらも**
**useRecoilValue:値のみ**
**useSetRecoilState:更新関数のみ**

状況に応じて使い分けましょう
#useContextと比べてrecoilの良い点

**recoilでは状態ごとにproviderで包む必要がない**
useContextと違ってRecoilRootというコンポーネントで包むだけでその配下のコンポーネントで状態を使用することが出来ます。いちいちproviderを作成することから解放されます。

```react:AuthProvider.tsx
export const App: VFC = () => {
    return (
        <RecoilRoot>
            <StylesProvider injectFirst>
                <MuiThemeProvider theme={theme}>
                    <ComponentRouter />
                </MuiThemeProvider>
            </StylesProvider>
        </RecoilRoot>
    );
};
```
だいぶ小さい再生ボタンになりました。

**状態の定義を１ファイルにまとめられる**
useContextでも出来なくは無かったですが、性質上パフォーマンスが落ちること、長すぎて可読性が低すぎることから現実的ではありませんでした。いちいち新しいファイルを作成することがなく非常に快適です。

**よく使う型の状態は定義しなくても使用できる**
これを使うかは賛否両論あるとは思いますが、recoilにはatomのほかにatomFamilyというものがあります。これは型が同じ状態を一つの定義で使い回せるようにするためのものです。

```react
export const booleanState = atomFamily<boolean, string>({
    key: "booleanState",
    default: false,
});
```
これはboolean型の状態の定義です。atomFamilyの第一引数が状態の型、第二引数がkeyの型です。keyは状態を一意に識別するためのものであり、一意にする必要があります。これをもちいてログイン状態を表すboolean型の状態isLoginを生成してみます。

```react
const [isLogin, setIsLogin] = useRecoilState(booleanState("isLogin"));
```
これでkey="isLogin"で識別出来るboolean型の状態が登録されました。このkeyの部分は別にファイルを生成してそこからimportするなど、色々工夫は必要そうですが、同じ型の状態を何回も定義する必要がないのは便利です。

**導入が非常に簡単**
今日recoil導入を決めて、移行し、この記事を書いています。かなり直感的でシンプルです。少なくともuseContextを使うよりはrecoilを使ったほうが良いと感じました。

#recoilの不満点
**現状vscodeのインテリセンスが利用できない**
recoilの拡張機能はほとんど存在せず、インテリセンスが利用できていないため、状態名を手打ちする必要があります。一応ESlintは公式が配布していたと思いますが、予測して出してくれると楽です。

使ってみての不満点はこれくらいです。

#まとめ
useContext使うくらいならrecoilでいいですね。Reduxならこうはいかなかったでしょうし、Lineなどでも導入していました。導入は簡単ですし、今かなり熱い状態管理ライブラリだと思うので、この機会に触ってみてはどうでしょうか。

#参考文献
[1]:https://recoiljs.org/

[[1]]:Recoil