Atom Family
前面提到了在组件中动态创建 atom 有一种更好的方法,这就是 Atom Family。Atom Family 是jotai/utils
中提供的一种扩展 atom 构建方法,可以根据给定的参数动态的创建 atom。Atom Family 使用atomFamily
函数创建,这个函数针对创建的 atom 不同,有两种函数签名。
// 针对要创建的atom是原始atom
function atomFamily<Param, Value>(
initializeAtom: (param: Param) =Atom<Value>,
areEqual?: (a: Param, b: Param) => boolean
): (param: Param) => Atom<Value>;
// 针对要创建的atom是派生atom,可以使用更多的自定义内容
function atomFamily<Param, Value, Update>(
initializeAtom: (param: Param) => WritableAtom<Value, Update>,
areEqual?: (a: Param, b: Param) => boolean
): (param: Param) => WritableAtom<Value, Update>;
以上atomFamily
的函数签名可以在使用 TypeScript 时显式标记泛型参数使用。但是注意如果需要创建的 atom 是一个原始 atom,那么需要仿照以下示例中的类型来辅助描述Update
类型参数。
type SetStateAction<Value> = Value | ((prev: Value) => Value);
const nameFamily = atomFamily<string, string, SetStateAction<string>>(
(name: string) => atom(name)
);
Atom Family 在底层使用一个 Map 来维护所提供的Param
与创建的 atom 之间的对应关系,如果提供的param
经过areEqual
参数提供的一致性判定,那么atomFamily
将会将缓存的 atom 提供出来。在默认情况下调用atomFamily
时不需要提供areEqual
参数,Jotai 默认会使用Object.is
来完成param
的一致性判定。
以下通过一个较为复杂的示例来说明 Atom Family 的使用。
const productAmountRecordAtom = atom<Record<string, number>>({});
const productAmountAtom = atomFamily<string, number, SetStateAction<number>>(
(name) =>
atom(
(get) => get(productAmountRecordAtom)[name] ?? null,
(get, set, amount) => {
const prev = get(productAmountRecordAtom);
set(productAmountRecordAtom, {
...prev,
[name]: amount,
});
}
)
);
const ProductDetail: FC<{ name: string }> = ({ name }) => {
const [amount, setAmount] = useAtom(productAmountAtom(name));
// 以下省略React组件的实现以及其他操作代码
};