atomFamily(選項)
傳回傳回可寫入 RecoilState
原子 的函數。
function atomFamily<T, P: Parameter>({
key: string,
default?:
| T
| Promise<T>
| Loadable<T>
| WrappedValue<T>
| RecoilValue<T>
| (P => T | Promise<T> | Loadable<T> | WrappedValue<T> | RecoilValue<T>),
effects?:
| $ReadOnlyArray<AtomEffect<T>>
| (P => $ReadOnlyArray<AtomEffect<T>>),
dangerouslyAllowMutability?: boolean,
}): P => RecoilState<T>
金鑰
- 用於在內部識別原子的獨特字串。這個字串應與整個應用程式中的其他原子和選擇器不同。預設值
- 原子的初始值。如同原子,它可以是值本身、Promise
、Loadable
、包裝的值或另一個代表預設值的原子/選擇器。原子群也可以是用來傳遞參數並傳回該群組成員的預設值的函數。倘若未提供預設值,原子將會處於待處理狀態觸發 Suspense。效果
- 可選陣列或用來根據群組參數取得陣列的回呼函式,內容為原子效果。dangerouslyAllowMutability
- Recoil 根據原子狀態的變更,得知何時要通知使用原子的元件進行重新渲染。如果原子的值已變動,它可能會繞過這個機制,並在沒有妥善通知訂閱元件的情況下,導致狀態變更。為協助防範此情況,所有儲存值皆已凍結。在某些情況下,可能會希望使用此選項來覆寫這點。
atom
代表 Recoil 中的一小段狀態。您的應用程式會為每個 <RecoilRoot>
建立並註冊一個原子。但是,如果您的狀態不是全域性的話怎麼辦?如果您的狀態與控制項的特定執行個體或特定元素有關又該怎麼辦?例如,也許您的應用程式是一個使用者介面原型製作工具,其中使用者可以動態新增元素,且每個元素都有其狀態,例如位置。理想情況下,每個元素應取得它自己的狀態原子。您可以透過記憶模式實作此功能。不過,Recoil 提供 atomFamily()
公用程式,可為您提供此模式。原子群集代表一系列原子。當您呼叫 atomFamily()
時,它會傳回一個函式,其中會根據您傳入的參數,提供 RecoilState
原子。
參數類型
type Primitive = void | null | boolean | number | string;
interface HasToJSON {
toJSON(): Parameter;
}
type Parameter =
| Primitive
| HasToJSON
| $ReadOnlyArray<Parameter>
| $ReadOnly<{[string]: Parameter}>
| $ReadOnlySet<Parameter>
| $ReadOnlyMap<Parameter, Parameter>;
atomFamily()
基本上提供從參數到原子的對應。您只需要提供一個原子群集的單一金鑰,而它會為每個基礎原子產生一個唯一的金鑰。這些原子金鑰可用於持續性,且必須在應用程式執行期間保持穩定。
對您可用於群集 Parameter
的類型設有某些限制。它們可能會在不同的呼叫位置產生,而我們希望等效參數來參考同一個基礎原子。因此,參數會使用相等性比較值,且必須可序列化。若要可序列化,它必須是
- 基本值
- 可序列化值陣列、物件、
Map
或Set
- 包含與
JSON.stringify()
類似,會傳回可序列化值之toJSON()
方法
範例
const elementPositionStateFamily = atomFamily({
key: 'ElementPosition',
default: [0, 0],
});
function ElementListItem({elementID}) {
const position = useRecoilValue(elementPositionStateFamily(elementID));
return (
<div>
Element: {elementID}
Position: {position}
</div>
);
}
群集預設值
atomFamily()
所需的選項與簡單的 atom()
大致相同。但預設值也可以進行參數化。這表示您可以提供一個函式,其中會接收參數值並傳回實際預設值。例如
const myAtomFamily = atomFamily({
key: ‘MyAtom’,
default: param => defaultBasedOnParam(param),
});
若要根據其他狀態建立動態預設值,請使用 selectorFamily()
,它也可以存取參數值。請勿僅針對 atomFamily()
預設值使用 selector()
,因為它會產生重複的金鑰。
const myAtomFamily = atomFamily({
key: ‘MyAtom’,
default: selectorFamily({
key: 'MyAtom/Default',
get: param => ({get}) => {
const otherAtomValue = get(otherState);
return computeDefaultUsingParam(otherAtomValue, param);
},
}),
});
訂閱
使用這個方式,為每一個元素使用分開的 atom,相較於嘗試儲存單一的 atom 和一份所有元素的狀態映射,有一個好處,在於每個元素都能維護其個別的訂閱。因此,只更新一個元素的值,只會導致訂閱這個 atom 的 React 組件被更新。
範圍 atom
有時你可能希望根據某個其他屬性、「React Context」,或某塊狀態來「設定範圍」atom 狀態。例如
const viewWidthForPaneState = atomFamily<number, PaneID>({
key: 'ViewWidthForPane',
default: 42,
});
function PaneView() {
const paneID = useContext(PaneIDContext);
const viewWidth = useRecoilValue(viewWidthForPaneState(paneID));
...
}
如果你希望根據某個其他 Recoil 狀態設定範圍,並希望在每個呼叫站點避免查詢範圍參數,那麼使用一個包裝器 selector()
可能是個有用的模式
const viewWidthState = selector({
key: 'ViewWidth',
get: ({get}) => viewWidthForPane(get(currentPaneState)),
set: ({get, set}, newValue) => set(viewWidthForPane(get(currentPaneState)), newValue),
});
function PaneView() {
const viewWidth = useRecoilValue(viewWidthState);
...
}
持久化
持久化觀察者和 atomic effect 會將每個參數值的狀態同步為不同的 atom,並透過使用參數值的序列化產生唯一的金鑰。因此,選用 可序列化參數 非常重要。不允許自訂類別或函數。