LoginSignup
1
1

React + TypeScript: Facebookの状態管理ライブラリRecoilを使ってみる

Last updated at Posted at 2022-01-25

RecoilはReactの状態を管理するライブラリで、Facebook改めMetaが開発しています。本稿執筆時の最新バージョンは0.7.7です。状態をひとまとめにするのではなく、ひとつひとつ細かく分けて管理します。ざっくりとお伝えするなら、useStateの状態をコンポーネントツリー内にまたがって共有するイメージです。

本稿は公式サイトの「Getting Started」で紹介されたコードサンプルを、モジュールに分けた作例に書き替えて解説します(サンプル001)。また、TypeScriptによる型づけも加えました。

サンプル001■React + TypeScript: Recoil example

React + TypeScriptのひな形アプリケーションをつくる

Reactアプリケーションのひな形は、Create React Appでつくることにします。オプションとして--template typescriptを加えれば、TypeScriptの環境が簡単に加わえられて便利です(「Create React AppでTypeScriptが加わったひな形アプリケーションをつくる」参照)。

インストール

Recoilは、npmあるいはyarnでつぎのようにインストールしてください。

npm install recoil
yarn add recoil

コンポーネントツリーを<RecoilRoot>でつつむ

まず、状態を共有したいコンポーネントツリーのルートは、<RecoilRoot>で包んでください。すべての子孫コンポーネントから、同一の状態が使えるようになります。ここでは、親コンポーネントをCharacterCounterとしました。

src/App.tsx
import { RecoilRoot } from 'recoil';
import { CharacterCounter } from './CharacterCounter';

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

atomで状態を定める

atom()は、状態をひとつひとつ定める関数です。<RecoilRoot>に包まれたツリー内のすべてのコンポーネントから、同じ状態の値が読み書きできます。atomの値を参照するコンポーネントは、暗黙的に購読対象となる仕組みです。atomが更新されると、購読対象のコンポーネントは再描画されます。atomに渡すオプションオブジェクトには、一意の識別子keyとデフォルト値defaultを与えてください。

src/textState.ts
import { atom } from 'recoil';

export const textState = atom({
	key: 'textState', // 他のatomやselectorに対して一意のID
	default: '', // デフォルト値(初期値)
});

<RecoilRoot>に包んだ親コンポーネントCharacterCounterには、このあとふたつの子コンポーネント(TextInputCharacterCount)を定めて加えます。

src/CharacterCounter.tsx
import { TextInput } from './TextInput';
import { CharacterCount } from './CharacterCount';

export const CharacterCounter: React.FC = () => {
	return (
		<div>
			<TextInput />
			<CharacterCount />
		</div>
	);
};

コンポーネントからatomの読み書きをするために用いるのがuseRecoilState()フックです。構文はuseStateと同じで、コンポーネントツリーの外にある状態を共有して使えます。

状態がコンポーネントの外にあることを除けば、useStateフックとやっていることは変わりません。<input type="text">要素に入力した値が設定関数setText()で状態を書き替え、状態変数textの値はテキストとして表示されます。

src/TextInput.tsx
import { useCallback } from "react";
import { useRecoilState } from 'recoil';
import { textState } from './textState';

export const TextInput: React.VFC = () => {
	const [text, setText] = useRecoilState(textState);
	const onChange: React.ChangeEventHandler<HTMLInputElement> = useCallback(
		({ target: { value } }) => {
			setText(value);
		},
		[setText]
	);
	return (
		<div>
			<input type="text" value={text} onChange={onChange} />
			<br />
			Echo: {text}
		</div>
	);
};

selectorで状態の値に手を加えて返す

複数のatomから参照した値に手を加えて返すのがselector()です。他のselectorから値を得ることもできます。派生の状態をつくる純粋な関数です。引数として渡すオプションオブジェクトのget()に定めたコールバックは、状態の値にもとづいて処理した結果を返します。コールバックの引数(get)のget()atom(textState)から値を参照するための関数です。

charCountStatetextStateからtextを得て、その文字数を返します。

src/charCountState.ts
import { selector } from 'recoil';
import { textState } from './textState';

export const charCountState = selector({
	key: 'charCountState', // 他のatomやselectorに対して一意のID
	// 状態の値にもとづいて処理した結果を返す
	get: ({ get }) => {
		const text = get(textState);
		return text.length;
	},
});

selectorの返す値を読み込むには、フックuseRecoilValue()を用いてください。コンポーネントCharacterCountが表示するのは、textStateに設定された文字数です。でき上がった作例の動きは、冒頭のサンプル001でご確認いただけます。

src/CharacterCount.tsx
import { useRecoilValue } from 'recoil';
import { charCountState } from './charCountState';

export const CharacterCount: React.FC = () => {
	const count = useRecoilValue(charCountState);
	return <>Character Count: {count}</>;
};
1
1
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
1
1