本文へ移動

Recoil 0.4

·4分間の読書

設定可能なセレクターキャッシュ、複数のatomによるトランザクションのためのAPIの改善、その他の最適化と修正を含むRecoil 0.4のリリースを発表できることを嬉しく思います。

設定可能なセレクターキャッシュ

cachePolicy_UNSTABLEプロパティは、セレクターセレクターファミリーにおいて、セレクターの内部キャッシュのキャッシュ動作を設定できます。このプロパティは、多数のセレクターを持つアプリケーションや、変化する依存関係の多いセレクターを持つアプリケーションにおいて、メモリ消費量を削減するのに役立ちます。

この新しいプロパティの使用方法の例を以下に示します。

const clockState = selector({
key: 'clockState',
get: ({get}) => {
const hour = get(hourState);
const minute = get(minuteState);
const second = get(secondState); // will re-run every second

return `${hour}:${minute}:${second}`;
},
cachePolicy_UNSTABLE: {
// Only store the most recent set of dependencies and their values
eviction: 'most-recent',
},
});

上記の例では、clockStateは毎秒再計算され、内部キャッシュに新しい依存値のセットが追加されます。これにより、内部キャッシュが無限に増加するため、時間の経過とともにメモリの問題が発生する可能性があります。most-recent削除ポリシーを使用すると、内部セレクターキャッシュには、最新の依存関係のセットとその値、およびそれらの依存関係に基づく実際のセレクター値のみが保持されるため、メモリの問題が解決されます。

現在の削除オプションは次のとおりです。

  • lru - サイズがmaxSizeを超えた場合、キャッシュから最後に使用された値を削除します。
  • most-recent - 最新の値のみを保持します。
  • keep-all (デフォルト) - キャッシュ内のすべてのエントリを保持し、削除しません。

注記:デフォルトの削除ポリシー(現在はkeep-all)は将来変更される可能性があります。

複数のatomによるトランザクション

複数のatomを単一のトランザクションとしてまとめて更新するための改良されたAPIを紹介します。新しいuseRecoilTransaction_UNSTABLE()フックは、以前よりも簡単で、効率的で、安全です。この新しいフックは最終的にuseRecoilCallback()のほとんどの使用を置き換える必要がありますが、このリリースは特定の制限がある最初の実装に過ぎず、将来のリリースで対処されます。

2つのatom、positionStateheadingStateがあり、単一のアクションの一部としてそれらをまとめて更新したいとします。ここで、positionStateの新しい値は、positionStateheadingStateの現在の値の両方に関数です。これは、副作用のない純粋関数である必要があるトランザクションで実現できます。

const goForward = useRecoilTransaction_UNSTABLE(({get, set}) => (distance) => {
const heading = get(headingState);
const position = get(positionState);
set(positionAtom, {
x: position.x + cos(heading) * distance,
y: position.y + sin(heading) * distance,
});
});

イベントハンドラーでgoForward(distance)を呼び出すだけで、トランザクションを実行できます。これにより、コンポーネントがレンダリングされたときの状態ではなく、現在の値に基づいて状態が更新されます。トランザクション中に以前の書き込みの値を読むこともできます。更新プログラムの実行中は他の更新がコミットされないため、一貫性のある状態ストアが表示されます。

useRecoilCallback()を使用した以前のアプローチは、次のようになっている可能性があります。

const goForward = useRecoilCallback(({snapshot, gotoSnapshot}) => (distance) => {
const mutatedSnapshot = snapshot.map(({get, set}) => {
const heading = get(headingState);
const position = get(positionState);
set(positionState, {
x: position.x + cos(heading) * distance,
y: position.y + sin(heading) * distance,
});
});
gotoSnapshot(mutatedSnapshot);
});

これには、次の欠点があります。

  • スナップショットの完全な汎用性を管理するためのパフォーマンスオーバーヘッドがあります。
  • バグが発生する可能性が高くなります。スナップショットは保持され、将来使用される可能性があります。スナップショットには変更セットだけでなく、Recoil状態の完全なセットが含まれているため、スナップショットの作成とコミットの間に発生した変更を誤って巻き戻す可能性があります。

Reducerの例

このフックを使用して、複数のatomに対するアクションを実行するReducerパターンを作成することもできます。

const reducer = useRecoilTransaction_UNSTABLE(({get, set}) => action => {
switch(action.type) {
case 'goForward':
const heading = get(headingState);
set(positionState, position => {
x: position.x + cos(heading) * action.distance,
y: position.y + sin(heading) * action.distance,
});
break;

case 'turn':
set(headingState, action.heading);
break;
}
});

修正と最適化

  • selectorFamily()getCallback()useGetRecoilValueInfo()Snapshot#getNodes()のTypeScript型付けを修正しました(#1060#1116#1123)
  • waitFor*()ヘルパー(例:waitForAll())で、セレクターオプションのdangerouslyAllowMutabilityを介して有効化されたミュータブルな値をセレクターで使用できるようにしました(#1074#1096)
  • Atom Effectsの修正
    • atomがリセットされた場合、または非同期デフォルトPromiseが解決された場合に、onSet()ハンドラーが適切な新しい値を取得するように修正しました(#1059#1050#738)(これは、DefaultValueプレースホルダーではなく、実際の新しい値がハンドラーに提供されるため、わずかな破壊的変更です)
    • 複数のAtom Effectsクリーンアップハンドラーのサポートを修正しました(#1125)
    • Snapshotを介して初期化された効果のあるatomの場合のセレクターサブスクリプションを修正しました(#1135#1107)
  • 依存関係がキャッシュされた値に解決される場合の非同期セレクターの最適化(#1037)
  • 不要な警告メッセージを削除しました(#1034#1062)