3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Recoilに入門してみる

Last updated at Posted at 2023-01-30

なにかと話題になるRecoilですが、未だ触ったことがなかったので公式ドキュメントを読みつつ、入門してみたいと思います。

本記事の目的

本記事で扱わないこと

  • Recoilの応用
  • Recoilの運用や設計
  • 他ライブラリとの比較

Recoilとは

RecoilはReactの状態管理ライブラリです。Reactにおいてグローバルな状態をどう管理するかというのは悩ましい話題の一つです。ビルドインで備わっている状態管理機構(Context APIやpropsのバケツリレー)もありますが、それはそれでいくつかの制約があります(詳しくはドキュメントのMotivationをご参照ください)。Recoilは、そのような問題を改善するために開発されているという背景があるようです。

Recoilに入門する

Recoilの重要な概念としてAtomとSelectorが存在します。本記事ではこのAtomとSelectorの基本について学んでいきます。

RecoilRoot

まず最初の準備としてRecoilを使用するためには、どこかの祖先・親コンポーネントでRecoilRootを使用する必要があります。

import { RecoilRoot } from 'recoil';
import { CharacterCounter } from './components/CharacterCounter';

const App = () => {
  return (
    <RecoilRoot>
      {/* この中でRecoilが使える */}
      <CharacterCounter />
    </RecoilRoot>
  );
};

export default App;

Atom

Atomは状態の一部を表します。Atomは任意のコンポーネントから状態の読み書きをすることができます。あるコンポーネントからAtomの状態を書き換えると、そのAtomを読み込んでいるすべてのコンポーネントが再レンダリングされ、書き込んだ状態が反映されます。いわゆるグローバルな状態をAtomに保持することが可能です。

import { atom } from 'recoil';
const textState = atom({
  key: 'textState', // ユニークなID
  default: '', // デフォルト値
});

keyにはユニークなID(他のAtomやSelectorと被らない値)を設定する必要があります。defaultにデフォルト値を設定することができます。

Atomへのアクセス

コンポーネントからAtomにアクセスするためにはフックを使う必要があります。

状態の読み取り

Atomから状態を読み取るにはuseRecoilValueフックを使うことができます。次の例ではEcho: Helloの文字列が表示されると思います。

import { atom, useRecoilValue } from 'recoil';

const textState = atom({
  key: 'textState',
  default: 'Hello',
});

export const CharacterCounter = () => {
  return (
    <div>
      <TextInput />
    </div>
  );
};

const TextInput = () => {
  // useRecoilValue()の引数にAtomを渡す
  const text = useRecoilValue(textState);
  return <div>Echo: {text}</div>;
};

状態の書き込み

Atomへ状態を書き込むにはuseSetRecoilStateを使うことができます。次の例ではinputの値を書き換えることによって、Atomの値が更新されることを確認できます。

import { atom, useRecoilValue, useSetRecoilState } from 'recoil';

const textState = atom({
  key: 'textState',
  default: 'Hello',
});

export const CharacterCounter = () => {
  return (
    <div>
      <TextInput />
    </div>
  );
};

const TextInput = () => {
  const text = useRecoilValue(textState);
  // useSetRecoilState()の引数にAtomを渡す
  const setText = useSetRecoilState(textState);
  return (
    <div>
      <input type="text" value={text} onChange={(e) => { setText(e.target.value) }}
      />
      <br />
      Echo: {text}
    </div>
  );
};

状態の読み取りと書き込み

Atomから状態を読み取る、Atomへ状態を書き込む、両方の操作を行うフックとしてuseRecoilStateを使うこともできます。useRecoilStateの見た目はuseStateに似ているので、非常にとっつきやすいと思います。ただし引数に初期値ではなく、Atomを渡します。

import { atom, useRecoilState } from 'recoil';

const textState = atom({
  key: 'textState',
  default: 'Hello',
});

export const CharacterCounter = () => {
  return (
    <div>
      <TextInput />
    </div>
  );
};

const TextInput = () => {
  // useRecoilState()の引数にAtomを渡す
  const [text, setText] = useRecoilState(textState);
  return (
    <div>
      <input type="text" value={text} onChange={(e) => { setText(e.target.value) }}
      />
      <br />
      Echo: {text}
    </div>
  );
};

Selector

ドキュメントによるとSelectorは「derived state」と記述されていました。翻訳にかけてみると「派生した状態」という意味になるようです。

派生した状態とは「transformation」、つまり何かしらのAtomを変換・加工した値のことです。Selectorは純粋関数でAtom(および他のSelector)を変換・加工した出力と捉えることができます。

import { selector } from 'recoil';
const charCountState = selector({
  key: 'charCountState', // ユニークなID
  get: ({get}) => {
    // 文字列を取得し、文字列の長さを返す
    const text = get(textState);
    return text.length;
  },
});

Atomと同様にkeyにはユニークなIDを設定する必要があります。上記の例ではgetを設定しています。getに渡された関数はAtomおよび他のSelectorにアクセスできます。

Selectorへのアクセス

Atomと同様に、Selectorへのアクセスもフックを使う必要があります。

Selectorから派生状態を取得する

派生状態の取得にはuseRecoilValueを使用することができます。ここでAtomの状態の読み取りでもuseRecoilValueを使用したことに気づくかもしれません。そうです、Atomの読み取りと同様のフックを使用して、Selectorの派生状態を取得することができます。

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

const textState = atom({
  key: 'textState',
  default: 'Hello',
});

const charCountState = selector({
  key: 'charCountState',
  get: ({get}) => {
    const text = get(textState);
    return text.length;
  },
});

export const CharacterCounter = () => {
  return (
    <div>
      <TextInput />
      <CharacterCount />
    </div>
  );

// ...略

const CharacterCount = () => {
  // useRecoilValue()の引数にselectorを渡す
  const count = useRecoilValue(charCountState);
  return <>Character Count: {count}</>;
}

Selectorを通して状態を更新する

今回のコードでは登場しませんが、Selectorを通して加工した状態を書き込むことも可能です。ただし、Atomとは違い、Selectorは常に書き込み可能というわけではありません。

書き込み可能なセレクターを生成するためには、setプロパティを設定する必要があります。

setプロパティの例
const proxySelector = selector({
  key: 'ProxySelector',
  get: ({get}) => ({...get(myAtom), extraField: 'hi'}),
  set: ({set}, newValue) => set(myAtom, newValue),
});

Atomと同様に、書き込み可能なSelectorはuseSetRecoilStateuseRecoilStateを使用して、加工した状態を書き込むことができます。

おわりに

Recoilに入門してみるということで、AtomとSelectorの基本的な部分に関してドキュメントを読みつつまとめてみました。本記事で取り上げた内容は基本中の基本であり、Recoilのほんの一部しかすぎませんが、ドキュメントも読みやすく、APIもシンプルで、非常に使いやすく感じました。気になっているけどまだ触ったことないという方は、是非触ってみてください!

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?