跳到主要內容

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>
  • 金鑰 - 用於在內部識別原子的獨特字串。這個字串應與整個應用程式中的其他原子和選擇器不同。
  • 預設值 - 原子的初始值。如同原子,它可以是值本身、PromiseLoadable、包裝的值或另一個代表預設值的原子/選擇器。原子群也可以是用來傳遞參數並傳回該群組成員的預設值的函數。倘若未提供預設值,原子將會處於待處理狀態觸發 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 的類型設有某些限制。它們可能會在不同的呼叫位置產生,而我們希望等效參數來參考同一個基礎原子。因此,參數會使用相等性比較值,且必須可序列化。若要可序列化,它必須是

  • 基本值
  • 可序列化值陣列、物件、MapSet
  • 包含與 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,並透過使用參數值的序列化產生唯一的金鑰。因此,選用 可序列化參數 非常重要。不允許自訂類別或函數。