useRecoilCallback
何らかのアクションに応じて Recoil state を読み書きしたいが、パフォーマンスは犠牲にしたくない場合に用いる Hooks
useRecoilCallback を理解する前に、Snapshot インターフェースについて理解しておくと良いかも知れない useRecoilCallback は React.useCallback と良く似たインターフェースをしている
code:useCallback.js
const func = useCallback((...params) => {}, deps); // func(...params)
code:useRecoilCallback.js
const func = useRecoilCallback((api) => (...params) => {}, deps); // func(...params)
callback のパラメータとして、Recoil state にアクセスするための API を受け取れる
snapshot: Recoil state の "当時の" 値にアクセスするための Snapshot 主に次のようなケースで用いられる
何らかのユーザーアクションが発生したあとで Recoil state の取得を開始し、完了するまで待ちたいケース
e.g. リンクをクリックしたとき、次のページで必要なデータを pre-fetch してからページを移動する
Recoil state の value にアクセスしたいが、value が更新されてもコンポーネントを再描画したくないケース
e.g. アナリティクスのために Recoil state の現在の value をバックグラウンドでサーバに送信する
e.g. ローカルで頻繁に発生する変更を debounce しつつサーバにアップロードする (いわゆるオートセーブ)
Side effect を伴うケース
これは具体的にどういうときに問題になるのか、まだよく分かっていない teramotodaiki.icon
余談
用例
1. Submit ボタンの再描画コストを削減するために使う
code:Message.jsx
const text = atom({
key: 'inputMessage',
default: ''
})
function TextInput () {
const handleChange = useCallback(event => setValue(event.currentTarget.value), []);
return <input value={value} onChange={handleChange} />
}
function TextSubmit () {
const handleSubmit = useRecoilCallback(({ getPromise }) => async () => {
const value = await getPromise(text); // text は非同期ではないため、実際には待たない
if (!value) return; // 入力のバリデーション(テキストが空だった)
await fetch(/submit?value=${value}); // なんらかの非同期処理を行うことができる
}, []);
return (
<button onClick={handleSubmit}>Submit</button>
);
}
TextSubmit の描画に value が必要ない場合、 useRecoilCallback を使った方が再描画の回数を抑えられる
Recoil state はそもそもバッチ更新されるので、わざわざ useRecoilCallback を使うメリットはあまり高くない。TextSubmit の描画コストが高い場合はアドバンテージがあるかも知れない