Recoilの簡単な説明

# Recoilをつかってみた

Qiita投稿2回目です。

今回はRecoilを軽く利用したのでRecoilについて簡単に説明します。

私が利用したの` recoil 0.0.10 `です。

## Recoilとは
RecoilとはFacebookにより作成されたReact専用の状態管理ライブラリのこと。

## 状態管理とは
状態とはフロントの保持するデータのこと。

アプリケーションの規模が大きくなるほどコンポーネントでのデータの管理が複雑になるのでデータの状態を管理する必要があり、データを集中管理することを状態管理という。

Reactの場合、Redux　Vueの場合はVuexなどが存在する。

状態管理についてfluxアーキテクチャを調べるとより理解が深まると考えています。

## Recoilの特徴

- 簡単

- React専用の状態管理ライブラリ

- Reduxよりコンパクトな状態管理を行うことが可能

- カスタムフックとの相性が良い

- 現時点(2020/08/12)では正式リリースはされていない

上記がRecoilの簡単な説明です。

## Recoilの良い点

カスタムフックとの相性がとても良かったです。

状態管理を容易に行えるので、Reactを最近利用し始めた私でも簡単に利用できました。

Reactの状態管理ライブラリのなかでTypeScriptとの相性が良い。

## Recoilについて

- Atoms

- Selectors

上記2つがRecoilの主機能です。

### Atoms

Atomとはデータを管理する場所です。

Atomにはどのコンポーネントからでもアクセスすることが可能で、全てのコンポーネントにデータを渡すことができます。

### Selectors

Atomに格納されたデータを取り出す機能です。

データを全て取り出す場合やデータを元のまま取り出す場合はAtomのみでも可能ですがデータを指定して取り出す場合やデータを加工して取り出す場合にはSelectorを利用します。

また、Selectorには非同期の処理を行うことが可能なので大量のデータを処理する場合にはSelectorを利用します。

Selectorはエラーのハンドリングを行うことが可能でAtomからデータを抽出した際に失敗した場合にどう対処するかを指定することもできます。

私は[公式ドキュメント](https://recoiljs.org/)をTypeScriptで記述したのですが公式ドキュメントのTodoアプリではAtomのみでもアプリの機能は乏しいですがアプリ開発ができると感じました。

## Recoilの利用方法

今回は公式ドキュメントに記述されている内容をTypeScriptで記述し、説明します。

``` TypeScript　App.tsx
import React from 'react'
import { RecoilRoot } from 'recoil'
import { Sample } from './Sample'

export function App() {
  <RecoilRoot>
    <Sample />
  </RecoilRoot>
}
```

まずはルートコンポーネントにRecoilRootを記述します。

これによりStateを格コンポーネントに受け渡すことができるようになります。

※　RecoilRootは必ずとしてルートコンポーネントに記述する必要はありません。今回はルートコンポーネントに記述しましたが状態管理をしたいコンポーネント郡の親コンポーネントであれば構いません。

## Atoms

``` atom.ts
import { atom } from 'recoil';

type Sample = {
  id: number;
  title: string;
  content: string;
  completed: boolean;
}

const initialSample: Sample[] = [];

export const sampleListState = atom({
  key: 'sampleListState',
  default: initialSample,
});
```


上記がatomのサンプルコードです。

> const initialSample: Sample[] = [];

まずデータを保存する配列を用意します。

```TypeScript
export const sampleListState = atom({
  key: 'sampleListState',
  default: initialSample,
});
```

ここがatomのメインとなる部分です。

defaultで先ほど記述したデータ格納場所を指定します。

これでatomでデータの保持ができるようになりました。

### useRecoilValueとuseSetRecoilState

useRecoilValueについて

> const sampleList = useRecoilValue(sampleListState);

上記コードにより、先ほど作成したsampleListStateに格納されたデータがsampleListに移動されました。

useRecoilValueによってatomに存在するデータをコンポーネントに渡すことができるようになります。

> const setSampleList = useSetRecoilState(sampleListState);

まずuseSetRecoilValueにsampleListStateにアクセスできるように設定します。

useSetRecoilStateによりatomに格納されているデータに追加することができます。

useRecoilValueとusseSetRecoilValueはReactに搭載されているuseStateの機能にとても似ていると思いました。

## Selectors

Atomsでデータの取り出しとデータの格納方法がわかりましたが、条件を指定してデータを取り出したい場合にはAtomsではできません。

そこでSelectorsを利用します。Selectorsを使用することで、条件を指定したデータを取り出すことができます。

```TypeScript
type Sample = {
  id: number;
  title: string;
  content: string;
  completed: boolean;
}

const initialSample: Sample[] = [];
```

Sampleデータにcompletedが格納されています。completedがtrueの場合とfalseの場合で取り出すデータを変えたい場合は

```TypeScript 
const sampleListFilterState = atom({
  key: 'sampleListFilterState',
  default: 'Show All',
});
```

まずはatomに全てのデータを参照できるようなコードを追加します。

ここからSelectorsの処理です。

``` selector.ts
import { selector } from 'recoil';
import { 
  sampleListState,
  sampleListFilterState
} from './atom';

type Sample = {
  id: number;
  title: string;
  content: string;
  completed: boolean;
}

const filterSampleListState = selector({
  key: 'filteredSampleListState',
  get: ({get}) => {
    const filter: string = get(sampleListFilterState);
    const list: Sample[] = get(sampleListState);

    switch (filter) {
      case 'Show Completed':
        return list.filter((item) => item.isComplete);
      case 'Show Uncompleted':
        return list.filter((item) => !item.isComplete);
      default:
        return list;
    }
  },
});
```

これによりfilterSampleListStateが選択された場合は、

> Show Completed

が選択された場合は、completedがtrueのデータの取り出しが可能になり、

> Show Uncompleted

が選択された場合は、completedがfalseのデータを取り出します。

では次はどちらのデータを取り出すかをコンポーネントで選択できるような処理を追加しましょう

```Sample.tsx
import React from 'react';
import { useRecoilValue } from 'recoil';
import { filterSampleListState } from './selector.ts'
import { SampleFilter } from './SampleItem';
import { SampleItem } from './SampleItem';

export function Sample() {
  const sampleList = useRecoilValue(filterSampleListState);
  return(
    <>
      <SampleFilter />
      {sampleList.map((sampleItem) => (
        <SampleItem item={sampleItem} key={sampleItem.id} />
      ))}
    </>
  )
}
```

上記により、sampleFilterで選択されたデータをSampleItemコンポーネントに渡すように設定されました。

ではデータをどうやって選択するかについて説明します。

```SampleFilter.tsx
import React from 'react';
import { useRecoilValue } from 'recoil';
import { sampleListFilterState } from './atom';


export function SampleFilter() {
  const [filters, setFilters] = useRecoilValue(sampleListFilterState);

  const updateFilter = (event: React.ChangeEvent<{ value: unknown }>) => {
    setFilter(event.target.value as string);
  }

  return (
    <select value={filter} onChange={updateFilter}>
      <option value="Show All">All</option>
      <option value="Show Completed">Completed</option>
      <option value="Show Uncompleted">Uncompleted</option>
    <select/>
  )
}
```

まず**useRecoilValue**に**sampleListFilterState**を設定します。

selectで3個の要素を選択することができます。selectの**value**はfilterSampleListStateにて記述した**switch**のcaseと一致します。

これで選んだデータがSampleItemコンポーネントにpropsで渡されるようになりました。

<br>

以上がSelectorsの簡単な使い方です。

## 参考サイト
[Recoil](https://recoiljs.org/)