3
2

More than 3 years have passed since last update.

この記事はCore Concepts の翻訳記事です。 (2020/11/15時点)

Overview

Recoil を使うと、atoms(共有ステート) から selectors(純粋関数) を通してReactコンポーネントに流れていくデータフローを構築できます。

Atoms は コンポーネントがsubscribeするステートの単位で、selectors は同期的または非同期的にステートを変形します。

Atoms

atomsはステートを表す単位となるものです。

atomsは更新可能でsubscribe可能な値です。つまりatomは文字通り更新できて、それに依存しているコンポーネントは更新時に新しいatomの値で再レンダリングされます。

また、atomsはランタイムによって構築されます。atomsはReactコンポーネントのローカルなステートと同じ場所で使うことができ、もし同じatomが複数のコンポーネントで利用されているとき、コンポーネントのatomは同じステートを共有します。

atomsは atom関数を使って作成します。

const fontSizeState = atom({
  key: 'fontSizeState',
  default: 14,
});

atomsの作成には一意なkeyを設定することが必要です。これはデバッグ、一貫性の維持、全てのatomsを俯瞰するためのadvanced APIなどで使われます。

もしatomsでkeyが重複する場合はエラーを起こしてしまうため、必ずこれは一意な値を使う必要があります。

またReactコンポーネントのステートと同様にデフォルト値を持ちます。

コンポーネントからatomを読み書きするためには、useRecoilStateと呼ばれるフックAPIを用います。

ReactのuseStateと見た目は似ていますが、ステートはatomsを使うコンポーネント間で共有されている点が異なっています。

function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  return (
    <button onClick={() => setFontSize((size) => size + 1)} style={{fontSize}}>
      Click to Enlarge
    </button>
  );
}

この例では、ボタンをクリックするとフォントサイズがインクリメントされます。しかもこれは、他のコンポーネントのフォントサイズにも波及します。

Selectors

selectorはatomsまたは他のselectorsを入力として受け取る純粋関数です。

上流つまり入力として渡したatomsやselectorsが更新されると、それを受け取るselector関数も再評価(更新)されます。

selectorsをsubscribeしているコンポーネントは、atomsのuseRecoilStateのように、selectorが更新されたときに再レンダリングされます。

selectors は ステートに基づいて派生データを計算するために使用されます。

selectorを使うことでreducerがステートを同期して有効に保つ必要がなくなり、余計なステートが増えることが回避できます。

その代わり、atomsには最低限必要なステートのみが格納され、他のステートはそこから派生する関数、つまりはselectorによって効率的に計算されます。

selectorはコンポーネントが何に依存しているかをトラックしてくれるおかげで関数的なアプローチがより効率的に取れるようになっています。

コンポーネントから見ると、selectorもatomも全く同じインターフェースをしているため、selectorとatomはお互いに置き換え可能になっています。

selectorsはselector関数を使って定義されます。

const fontSizeLabelState = selector({
  key: 'fontSizeLabelState',
  get: ({get}) => {
    const fontSize = get(fontSizeState);
    const unit = 'px';

    return `${fontSize}${unit}`;
  },
});

getプロパティは派生するステートを計算する関数です。この関数はatomsや他のselectorsの値を get() の引数を通して受け取ることができます。atomsやselectorsにアクセスすると依存関係が構築され、依存元が更新されたときに再計算が行われるようになっています。

今回の fontSizeLabelState ではselectorはfontSizeState atomにだけ依存しています。

コンセプト通り、fontSizeLabelState selectorは fontSizeState を入力として受け取って、フォーマットされたフォントサイズのラベルを出力する純粋関数のように振舞います。

selectorはuseRecoilValue()を使って読み取り可能です。これは引数にatomかselectorを受け取り、それに対応する値を返すAPIです。

fontSizeLabelState selectorは書き込み不可能なので、useRecoilState()は使用しません。 (書き込み可能なselectorについて知りたい場合はselector API reference参照)

function FontButton() {
  const [fontSize, setFontSize] = useRecoilState(fontSizeState);
  const fontSizeLabel = useRecoilValue(fontSizeLabelState);

  return (
    <>
      <div>Current font size: ${fontSizeLabel}</div>

      <button onClick={() => setFontSize(fontSize + 1)} style={{fontSize}}>
        Click to Enlarge
      </button>
    </>
  );
}

ボタンをクリックすると、ボタンのフォントサイズが増加し、現在のフォントサイズをフォーマットして表している${fontSizeLabel}にもその変更が反映されます。

3
2
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
2