跳至主要内容

atomFamily(options)

返回一个函数,该函数返回一个可写 RecoilState atom


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>
  • key - 用于在内部标识原子的唯一字符串。此字符串应相对于整个应用程序中的其他原子和选择器是唯一的。
  • default - 原子的初始值。与原子一样,它可以是直接的值,也可以是 PromiseLoadable、包装值或其他表示默认值的原子/选择器。原子族也可以是传递参数并返回该族成员默认值的函数。如果未提供,则原子将处于挂起状态并触发 Suspense。
  • effects - 一个可选的数组,或根据族参数获取数组的回调,用于 原子效果
  • dangerouslyAllowMutability - Recoil 依赖于原子状态更改来了解何时通知使用原子的组件重新渲染。如果原子的值被修改,它可能会绕过此操作并导致状态在未正确通知订阅组件的情况下发生更改。为了帮助防止这种情况,所有存储的值都被冻结。在某些情况下,可能需要使用此选项来覆盖此行为。

一个 atom 代表 Recoil 中的一块状态。原子由您的应用程序在每个 <RecoilRoot> 中创建和注册。但是,如果您的状态不是全局的怎么办?如果您的状态与控件的特定实例或特定元素相关联怎么办?例如,也许您的应用程序是一个 UI 原型工具,用户可以在其中动态添加元素,每个元素都有状态,例如其位置。理想情况下,每个元素都会获得自己的状态原子。您可以通过记忆模式自己实现这一点。但是,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
  • 包含一个 toJSON() 方法,该方法返回一个可序列化的值,类似于 JSON.stringify()

示例

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(),它也具有对参数值的访问权限。不要仅仅使用 selector() 来作为 atomFamily() 的默认值,因为它会产生重复的键。

const myAtomFamily = atomFamily({
key: ‘MyAtom’,
default: selectorFamily({
key: 'MyAtom/Default',
get: param => ({get}) => {
const otherAtomValue = get(otherState);
return computeDefaultUsingParam(otherAtomValue, param);
},
}),
});

订阅

使用此模式为每个元素创建单独的原子而不是尝试存储一个包含所有元素状态映射的原子的一大优势是它们都保持自己的独立订阅。因此,更新一个元素的值只会导致订阅了该原子的 React 组件进行更新。

范围原子

有时您可能希望通过其他道具、React 上下文或状态部分来“范围”原子状态。例如

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);
...
}

持久化

持久化观察者和 原子效果 将根据用于序列化参数值的参数值的唯一键,将每个参数值的州同步为一个独立的原子。因此,可序列化参数 非常重要。不允许使用自定义类或函数。