RecoilのGetting Startedを試してみた(TypeScript)

2022.04.11

こんにちは、CX事業本部 IoT事業部の若槻です。

以前のエントリでReactでGlobalに状態管理を行う方法としてuseContextを試してみました。

他方で「Reactで状態管理を行うならRecoilが便利だよ」というアドバイスをもらっていたので、今回はRecoilへの入門として公式のGetting StartedをTSプロジェクトで試してみました。

やってみる

こちらのGetting Startedを参考にRecoilをTSプロジェクトで動かしてみます。

Reactアプリの準備

create-react-appでReactアプリをTSプロジェクトで作成します。

$ npx create-react-app web --template typescript
$ cd web

これにより次のようなバージョンでReactアプリが作成できました。

  • react@18.0.0
  • typescript@4.6.3

インストール

Recoilをインストールします。

$ npm install recoil

まず動かしてみる

まず動くデモを作ってみます。src/App.tsxを次のように修正します。

src/App.tsx

import React from 'react';
import {
  RecoilRoot,
  atom,
  selector,
  useRecoilState,
  useRecoilValue,
} from 'recoil';

function App() {
  return (
    <RecoilRoot>
      <CharacterCounter />
    </RecoilRoot>
  );
}

export default App;

const textState = atom({
  key: 'textState', // unique ID (with respect to other atoms/selectors)
  default: '', // default value (aka initial value)
});

function CharacterCounter() {
  return (
    <div>
      <TextInput />
      <CharacterCount />
    </div>
  );
}

function TextInput() {
  const [text, setText] = useRecoilState(textState);

  const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setText(event.target.value);
  };

  return (
    <div>
      <input type='text' value={text} onChange={onChange} />
      <br />
      Echo: {text}
    </div>
  );
}

const charCountState = selector({
  key: 'charCountState', // unique ID (with respect to other atoms/selectors)
  get: ({ get }) => {
    const text = get(textState);

    return text.length;
  },
});

function CharacterCount() {
  const count = useRecoilValue(charCountState);

  return <>Character Count: {count}</>;
}

Reactアプリを実行します。

$ npm start

ブラウザでアプリが起動します。入力欄に文字を入力すると、入力した文字とカウントが表示される仕組みが作れました。

コード解説

RecoilRoot

Recoilによる状態管理を行いたいComponent treeの親をRecoilRootでラップする必要があります。

src/App.tsx

import React from 'react';
import {
  RecoilRoot,
  atom,
  selector,
  useRecoilState,
  useRecoilValue,
} from 'recoil';

function App() {
  return (
    <RecoilRoot>
      <CharacterCounter />
    </RecoilRoot>
  );
}

ポイントとして、ラップ時にその子Component内で使用したい状態やその値を指定する必要はありません。(useContextの場合は指定する必要がありました。)

Atom

Recoilではatomで作成したRecoilStateを状態として使用します。

src/App.tsx

const textState = atom({
  key: 'textState', // unique ID (with respect to other atoms/selectors)
  default: '', // default value (aka initial value)
});

RecoilStateはuseRecoilStateを使用して読み書きを行うHooksです。

src/App.tsx

function TextInput() {
  const [text, setText] = useRecoilState(textState);

  const onChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setText(event.target.value);
  };

  return (
    <div>
      <input type='text' value={text} onChange={onChange} />
      <br />
      Echo: {text}
    </div>
  );
}

状態の読み書きを組み込み関数で行えるのは便利ですね!

Selector

selectorを使用するとRecoilStateを変換した値を取得できるRecoilValueReadOnlyを作成できます。

src/App.tsx

const charCountState = selector({
  key: 'charCountState', // unique ID (with respect to other atoms/selectors)
  get: ({ get }) => {
    const text = get(textState);

    return text.length;
  },
});

RecoilValueReadOnlyからはuseRecoilValueを使用して値を読み取ることができます。

src/App.tsx

function CharacterCount() {
  const count = useRecoilValue(charCountState);

  return <>Character Count: {count}</>;
}

状態管理対象の値を変換して使用することが多い場合に役立ちそうです。

おわりに

RecoilのGetting StartedをTSプロジェクトで試してみました。

Reactの組み込みHooksであるuseContextと比べ、少ない記述でより簡単に状態管理が行えるという印象でした。

注意点として、2022年4月現在、Recoilはexperimentalとなっています。Repositoryのホストもまだfacebookexperimentalで行われています。

2020年6月にMeta社により開発が開始され、現在v0.7.0なのでGAまでもう一息というところでしょうか。GAが待ち遠しいですね。

参考

以上