メインコンテンツにスキップ

selector(options)

セレクターは、Recoilにおける関数、または派生状態を表します。それらは、副作用がなく、特定の依存関係の値のセットに対して常に同じ値を返す「冪等」または「純粋関数」と似ていると考えることができます。get関数のみが提供されている場合、セレクターは読み取り専用であり、RecoilValueReadOnlyオブジェクトを返します。setも提供されている場合は、書き込み可能なRecoilStateオブジェクトを返します。

Recoilは、そのセレクターにサブスクライブしているコンポーネントに再レンダリングを通知するタイミングを知るために、atomとセレクターの状態変更を管理します。セレクターのオブジェクト値を直接変更すると、これを回避し、サブスクライブしているコンポーネントに適切に通知されない可能性があります。バグを検出するために、Recoilは開発モードでセレクターの値オブジェクトをフリーズします。


function selector<T>({
key: string,

get: ({
get: GetRecoilValue,
getCallback: GetCallback,
}) => T | Promise<T> | Loadable<T> | WrappedValue<T> | RecoilValue<T>,

set?: (
{
get: GetRecoilValue,
set: SetRecoilState,
reset: ResetRecoilState,
},
newValue: T | DefaultValue,
) => void,

dangerouslyAllowMutability?: boolean,
cachePolicy_UNSTABLE?: CachePolicy,
})
type ValueOrUpdater<T> = T | DefaultValue | ((prevValue: T) => T | DefaultValue);
type GetCallback =
<Args, Return>(
callback: CallbackInterface => (...Args) => Return,
) => (...Args) => Return;

type GetRecoilValue = <T>(RecoilValue<T>) => T;
type SetRecoilState = <T>(RecoilState<T>, ValueOrUpdater<T>) => void;
type ResetRecoilState = <T>(RecoilState<T>) => void;

type CachePolicy =
| {eviction: 'lru', maxSize: number}
| {eviction: 'keep-all'}
| {eviction: 'most-recent'};
  • key - セレクターを内部で識別するために使用される一意の文字列。この文字列は、アプリケーション全体の他のatomおよびセレクターに対して一意である必要があります。永続化に使用する場合は、実行間で安定している必要があります。
  • get - 派生状態の値を評価する関数。値、非同期PromiseLoadable、または同じタイプを表す別のatomまたはセレクターを直接返すことができます。セレクター値をPromiseLoadableなどのように直接設定するには、selector.value(...)でラップできます。このコールバックには、以下のプロパティを含むオブジェクトが最初のパラメータとして渡されます。
    • get() - 他のatom/セレクターから値を取得するために使用される関数。この関数に渡されたすべてのatom/セレクターは、セレクターの依存関係のリストに暗黙的に追加されます。セレクターの依存関係が変更されると、セレクターは再評価されます。
    • getCallback() - コールバックインターフェースを持つRecoil対応のコールバックを作成するための関数。以下のを参照してください。
  • set? - このプロパティが設定されている場合、セレクターは書き込み可能な状態を返します。最初のパラメータとしてコールバックのオブジェクトと新しい入力値が渡される関数。入力値は、型Tの値であるか、ユーザーがセレクターをリセットした場合、型DefaultValueのオブジェクトである可能性があります。コールバックには以下が含まれます。
    • get() - 他のatom/セレクターから値を取得するために使用される関数。この関数は、セレクターを与えられたatom/セレクターにサブスクライブしません。
    • set() - アップストリームのRecoil状態の値を設定するために使用される関数。最初のパラメータはRecoil状態であり、2番目のパラメータは新しい値です。新しい値は、アップデーター関数であるか、リセットアクションを伝播するためのDefaultValueオブジェクトである可能性があります。
    • reset() - アップストリームのRecoil状態のデフォルト値にリセットするために使用される関数。唯一のパラメータはRecoil状態です。
  • dangerouslyAllowMutability - 場合によっては、状態の変化を表さないセレクターに格納されたオブジェクトの変更を許可することが望ましい場合があります。このオプションを使用して、開発モードでのオブジェクトのフリーズを上書きします。
  • cachePolicy_UNSTABLE - 内部セレクターキャッシュの動作を定義します。依存関係が多数変化するセレクターを持つアプリで、メモリフットプリントを制御するのに役立ちます。
    • eviction - lrumaxSizeを設定する必要がある)、keep-all(デフォルト)、またはmost-recentに設定できます。lruキャッシュは、キャッシュのサイズがmaxSizeを超えた場合に、セレクターキャッシュから最も使用頻度の低い値を削除します。keep-allポリシーは、すべてのセレクターの依存関係とその値がセレクターキャッシュに無期限に格納されることを意味します。most-recentポリシーは、サイズ1のキャッシュを使用し、最後に保存された依存関係のセットとその値のみを保持します。
    • キャッシュは、すべての依存関係とその値を含むキーに基づいてセレクターの値を格納することに注意してください。これは、内部セレクターキャッシュのサイズが、セレクターの値のサイズと、すべての依存関係の一意の値の数の両方に依存することを意味します。
    • デフォルトの削除ポリシー(現在はkeep-all)は、将来変更される可能性があることに注意してください。

単純な静的依存関係を持つセレクター

const mySelector = selector({
key: 'MySelector',
get: ({get}) => get(myAtom) * 100,
});

動的依存関係

読み取り専用セレクターには、依存関係に基づいてセレクターの値を評価するgetメソッドがあります。これらの依存関係のいずれかが更新されると、セレクターは再評価されます。依存関係は、セレクターを評価するときに実際に使用するatomまたはセレクターに基づいて動的に決定されます。前の依存関係の値に応じて、動的に異なる追加の依存関係を使用する場合があります。Recoilは、現在のデータフローグラフを自動的に更新し、セレクターが現在の依存関係のセットからの更新のみをサブスクライブするようにします。

この例では、mySelectortoggleState atomと、toggleStateの状態に応じてselectorAまたはselectorBのいずれかに依存します。

const toggleState = atom({key: 'Toggle', default: false});

const mySelector = selector({
key: 'MySelector',
get: ({get}) => {
const toggle = get(toggleState);
if (toggle) {
return get(selectorA);
} else {
return get(selectorB);
}
},
});

書き込み可能なセレクター

双方向セレクターは、入力値をパラメータとして受け取り、それを使用してデータフローグラフに沿って変更をアップストリームに伝播できます。ユーザーがセレクターを新しい値で設定するか、セレクターをリセットする可能性があるため、入力値はセレクターが表すものと同じ型であるか、リセットアクションを表すDefaultValueオブジェクトのいずれかです。

この単純なセレクターは、基本的にatomをラップして追加のフィールドを追加します。アップストリームのatomに設定およびリセット操作を渡すだけです。

const proxySelector = selector({
key: 'ProxySelector',
get: ({get}) => ({...get(myAtom), extraField: 'hi'}),
set: ({set}, newValue) => set(myAtom, newValue),
});

このセレクターはデータを変換するため、入力値がDefaultValueかどうかを確認する必要があります。

const transformSelector = selector({
key: 'TransformSelector',
get: ({get}) => get(myAtom) * 100,
set: ({set}, newValue) =>
set(myAtom, newValue instanceof DefaultValue ? newValue : newValue / 100),
});

非同期セレクター

セレクターは、非同期評価関数を持ち、出力値へのPromiseを返すこともできます。詳細については、このガイドを参照してください。

const myQuery = selector({
key: 'MyQuery',
get: async ({get}) => {
return await myAsyncQuery(get(queryParamState));
}
});

例(同期)

import {atom, selector, useRecoilState, DefaultValue, useResetRecoilState} from 'recoil';

const tempFahrenheit = atom({
key: 'tempFahrenheit',
default: 32,
});

const tempCelsius = selector({
key: 'tempCelsius',
get: ({get}) => ((get(tempFahrenheit) - 32) * 5) / 9,
set: ({set}, newValue) =>
set(
tempFahrenheit,
newValue instanceof DefaultValue ? newValue : (newValue * 9) / 5 + 32
),
});

function TempCelsius() {
const [tempF, setTempF] = useRecoilState(tempFahrenheit);
const [tempC, setTempC] = useRecoilState(tempCelsius);
const resetTemp = useResetRecoilState(tempCelsius);

const addTenCelsius = () => setTempC(tempC + 10);
const addTenFahrenheit = () => setTempF(tempF + 10);
const reset = () => resetTemp();

return (
<div>
Temp (Celsius): {tempC}
<br />
Temp (Fahrenheit): {tempF}
<br />
<button onClick={addTenCelsius}>Add 10 Celsius</button>
<br />
<button onClick={addTenFahrenheit}>Add 10 Fahrenheit</button>
<br />
<button onClick={reset}>Reset</button>
</div>
);
}

例(非同期)

import {selector, useRecoilValue} from 'recoil';

const myQuery = selector({
key: 'MyDBQuery',
get: async () => {
const response = await fetch(getMyRequestUrl());
return response.json();
},
});

function QueryResults() {
const queryResults = useRecoilValue(myQuery);

return (
<div>
{queryResults.foo}
</div>
);
}

function ResultsSection() {
return (
<React.Suspense fallback={<div>Loading...</div>}>
<QueryResults />
</React.Suspense>
);
}

より複雑な例については、このガイドを参照してください。

コールバックを持つオブジェクトを返す

場合によっては、セレクターを使用してコールバックを含むオブジェクトを返すことができます。これらのコールバックが、タイプアヘッド値のクエリやクリックハンドラーなど、Recoil状態にアクセスするのに役立つ場合があります。次の例では、セレクターを使用して、Recoil状態にアクセスするクリックハンドラーを持つメニュー項目を生成します。これは、これらのオブジェクトをReactコンポーネントのコンテキスト外のフレームワークまたはロジックに渡す場合に役立ちます。

このコールバックと、useRecoilCallback()の使用の間には対称性があります。getCallback()によって返されるコールバックは、後でRecoil状態にアクセスするための非同期コールバックとして使用できることに注意してください。セレクター値自体の評価中に呼び出すべきではありません。

const menuItemState = selectorFamily({
key: 'MenuItem',
get: itemID => ({get, getCallback}) => {
const name = get(itemNameQuery(itemID));
const onClick = getCallback(({snapshot}) => async () => {
const info = await snapshot.getPromise(itemInfoQuery(itemID));
displayInfoModal(info);
});
return {
title: `Show info for ${name}`,
onClick,
};
},
});

状態を変化させることができる例

const menuItemState = selectorFamily({
key: 'MenuItem',
get: itemID => ({get, getCallback}) => {
const name = get(itemNameQuery(itemID));
const onClick = getCallback(({refresh}) => () => {
refresh(itemInfoQuery(itemID));
});
return {
title: `Refresh data for ${name}`,
onClick,
};
},
});

キャッシュポリシーの構成

cachePolicy_UNSTABLEプロパティを使用すると、セレクターの内部キャッシュのキャッシュ動作を構成できます。このプロパティは、多数の変更される依存関係を持つ多数のセレクターがあるアプリケーションでメモリを削減するのに役立ちます。今のところ、構成可能なオプションはevictionのみですが、将来的にはさらに追加する可能性があります。

以下は、この新しいプロパティの使用方法の例です。

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)は、将来変更される可能性があります。