如何从反冲的atomFamily中获取所有元素?
How to get all elements from an atomFamily in recoil?
我第一次玩反冲,不知道如何从 atomFamily 读取所有元素。假设我有一个用户可以添加餐点的应用程序:
export const meals = atomFamily({
key: "meals",
default: {}
});
我可以按如下方式初始化一顿饭:
const [meal, setMeal] = useRecoilState(meals("bananas"));
const bananas = setMeal({name: "bananas", price: 5});
如何读取已添加到此 atomFamily 的所有项目?
您必须跟踪 atomFamily
的所有 ID 才能获取该家庭的所有成员。
请记住,这并不是真正的列表,更像是地图。
这样的事情应该会让你继续下去。
// atomFamily
const meals = atomFamily({
key: "meals",
default: {}
});
const mealIds = atom({
key: "mealsIds",
default: []
});
在族内创建新对象时,您还必须更新 mealIds
原子。
我通常使用 useRecoilCallback
挂钩将其同步在一起
const createMeal = useRecoilCallback(({ set }) => (mealId, price) => {
set(mealIds, currVal => [...currVal, mealId]);
set(meals(mealId), {name: mealId, price});
}, []);
这样您就可以通过调用创建餐点:
createMeal("bananas", 5);
并通过以下方式获取所有 ID:
const ids = useRecoilValue(mealIds);
您可以使用 selectorFamily
来抽象它,而不是使用 useRecoilCallback
。
// atomFamily
const mealsAtom = atomFamily({
key: "meals",
default: {}
});
const mealIds = atom({
key: "mealsIds",
default: []
});
// abstraction
const meals = selectorFamily({
key: "meals-access",
get: (id) => ({ get }) => {
const atom = get(mealsAtom(id));
return atom;
},
set: (id) => ({set}, meal) => {
set(mealsAtom(id), meal);
set(mealIds (id), prev => [...prev, meal.id)]);
}
});
此外,如果您想支持重置,可以使用以下代码:
// atomFamily
const mealsAtom = atomFamily({
key: "meals",
default: {}
});
const mealIds = atom({
key: "mealsIds",
default: []
});
// abstraction
const meals = selectorFamily({
key: "meals-access",
get: (id) => ({ get }) => {
const atom = get(mealsAtom(id));
return atom;
},
set: (id) => ({set, reset}, meal) => {
if(meal instanceof DefaultValue) {
// DefaultValue means reset context
reset(mealsAtom(id));
reset(mealIds (id));
return;
}
set(mealsAtom(id), meal);
set(mealIds (id), prev => [...prev, meal.id)]);
}
});
如果您使用的是 Typescript,您可以使用以下 guard.
使其更优雅
import { DefaultValue } from 'recoil';
export const guardRecoilDefaultValue = (
candidate: unknown
): candidate is DefaultValue => {
if (candidate instanceof DefaultValue) return true;
return false;
};
将此守卫与 Typescript 一起使用将类似于:
// atomFamily
const mealsAtom = atomFamily<IMeal, number>({
key: "meals",
default: {}
});
const mealIds = atom<number[]>({
key: "mealsIds",
default: []
});
// abstraction
const meals = selectorFamily<IMeal, number>({
key: "meals-access",
get: (id) => ({ get }) => {
const atom = get(mealsAtom(id));
return atom;
},
set: (id) => ({set, reset}, meal) => {
if (guardRecoilDefaultValue(meal)) {
// DefaultValue means reset context
reset(mealsAtom(id));
reset(mealIds (id));
return;
}
// from this line you got IMeal (not IMeal | DefaultValue)
set(mealsAtom(id), meal);
set(mealIds (id), prev => [...prev, meal.id)]);
}
});
以下是我在当前项目中使用它的方式:
(对于上下文,这是一个从字段选项对象数组创建的动态表单。表单值是通过 graphql 突变提交的,因此我们只希望进行最少的更改。因此,表单构建为用户编辑字段)
import { atom, atomFamily, DefaultValue, selectorFamily } from 'recoil';
type PossibleFormValue = string | null | undefined;
export const fieldStateAtom = atomFamily<PossibleFormValue, string>({
key: 'fieldState',
default: undefined,
});
export const fieldIdsAtom = atom<string[]>({
key: 'fieldIds',
default: [],
});
export const fieldStateSelector = selectorFamily<PossibleFormValue, string>({
key: 'fieldStateSelector',
get: (fieldId) => ({ get }) => get(fieldStateAtom(fieldId)),
set: (fieldId) => ({ set, get }, fieldValue) => {
set(fieldStateAtom(fieldId), fieldValue);
const fieldIds = get(fieldIdsAtom);
if (!fieldIds.includes(fieldId)) {
set(fieldIdsAtom, (prev) => [...prev, fieldId]);
}
},
});
export const formStateSelector = selectorFamily<
Record<string, PossibleFormValue>,
string[]
>({
key: 'formStateSelector',
get: (fieldIds) => ({ get }) => {
return fieldIds.reduce<Record<string, PossibleFormValue>>(
(result, fieldId) => {
const fieldValue = get(fieldStateAtom(fieldId));
return {
...result,
[fieldId]: fieldValue,
};
},
{},
);
},
set: (fieldIds) => ({ get, set, reset }, newValue) => {
if (newValue instanceof DefaultValue) {
reset(fieldIdsAtom);
const fieldIds = get(fieldIdsAtom);
fieldIds.forEach((fieldId) => reset(fieldStateAtom(fieldId)));
} else {
set(fieldIdsAtom, Object.keys(newValue));
fieldIds.forEach((fieldId) => {
set(fieldStateAtom(fieldId), newValue[fieldId]);
});
}
},
});
原子是选择器,在应用程序的 3 个地方使用:
字段组件中:
...
const localValue = useRecoilValue(fieldStateAtom(fieldId));
const setFieldValue = useSetRecoilState(fieldStateSelector(fieldId));
...
在保存处理组件中(尽管在具有显式提交按钮的表单中这可能更简单):
...
const fieldIds = useRecoilValue(fieldIdsAtom);
const formState = useRecoilValue(formStateSelector(fieldIds));
...
在另一个处理表单操作(包括表单重置)的组件中:
...
const resetFormState = useResetRecoilState(formStateSelector([]));
...
const handleDiscard = React.useCallback(() => {
...
resetFormState();
...
}, [..., resetFormState]);
您可以使用 atom
来跟踪 atomFamily
中每个原子的 ID。然后在 atomFamily
中添加或删除新原子时,使用 selectorFamily
或自定义函数用 id 列表更新原子。然后,具有 id 列表的原子可用于通过它们的 id 从 selectorFamily
.
中提取每个原子
// File for managing state
//Atom Family
export const mealsAtom = atomFamily({
key: "meals",
default: {},
});
//Atom ids list
export const mealsIds = atom({
key: "mealsIds",
default: [],
});
这是 selectorFamily
的样子:
// File for managing state
export const mealsSelector = selectorFamily({
key: "mealsSelector",
get: (mealId) => ({get}) => {
return get(meals(mealId));
},
set: (mealId) => ({set, reset}, newMeal) => {
// if 'newMeal' is an instance of Default value,
// the 'set' method will delete the atom from the atomFamily.
if (newMeal instanceof DefaultValue) {
// reset method deletes the atom from atomFamily. Then update ids list.
reset(mealsAtom(mealId));
set(mealsIds, (prevValue) => prevValue.filter((id) => id !== mealId));
} else {
// creates the atom and update the ids list
set(mealsAtom(mealId), newMeal);
set(mealsIds, (prev) => [...prev, mealId]);
}
},
});
现在,你如何使用所有这些?
- 创建一顿饭:
在这种情况下,我使用当前时间戳作为具有 Math.random()
的原子 ID
// Component to consume state
import {mealsSelector} from "your/path";
import {useSetRecoilState} from "recoil";
const setMeal = useSetRecoilState(mealsSelector(Math.random()));
setMeal({
name: "banana",
price: 5,
});
- 删除一顿饭:
// Component to consume state
import {mealsSelector} from "your/path";
import {DefaultValue, useSetRecoilState} from "recoil";
const setMeal = useSetRecoilState(mealsSelector(mealId));
setMeal(new DefaultValue());
- 从 atomFamily 中获取所有原子:
循环 id 列表并呈现接收 id 作为 props 的 Meals 组件,并使用它来获取每个原子的状态。
// Component to consume state, parent of Meals component
import {mealsIds} from "your/path";
import {useRecoilValue} from "recoil";
const mealIdsList = useRecoilValue(mealsIds);
//Inside the return function:
return(
{mealIdsList.slice()
.map((mealId) => (
<MealComponent
key={mealId}
id={mealId}
/>
))}
);
// Meal component to consume state
import {mealsSelector} from "your/path";
import {useRecoilValue} from "recoil";
const meal = useRecoilValue(mealsSelector(props.id));
然后,您有一个餐食组件列表,每个组件都有自己的状态,来自 atomFamily
。
我第一次玩反冲,不知道如何从 atomFamily 读取所有元素。假设我有一个用户可以添加餐点的应用程序:
export const meals = atomFamily({
key: "meals",
default: {}
});
我可以按如下方式初始化一顿饭:
const [meal, setMeal] = useRecoilState(meals("bananas"));
const bananas = setMeal({name: "bananas", price: 5});
如何读取已添加到此 atomFamily 的所有项目?
您必须跟踪 atomFamily
的所有 ID 才能获取该家庭的所有成员。
请记住,这并不是真正的列表,更像是地图。
这样的事情应该会让你继续下去。
// atomFamily
const meals = atomFamily({
key: "meals",
default: {}
});
const mealIds = atom({
key: "mealsIds",
default: []
});
在族内创建新对象时,您还必须更新 mealIds
原子。
我通常使用 useRecoilCallback
挂钩将其同步在一起
const createMeal = useRecoilCallback(({ set }) => (mealId, price) => {
set(mealIds, currVal => [...currVal, mealId]);
set(meals(mealId), {name: mealId, price});
}, []);
这样您就可以通过调用创建餐点:
createMeal("bananas", 5);
并通过以下方式获取所有 ID:
const ids = useRecoilValue(mealIds);
您可以使用 selectorFamily
来抽象它,而不是使用 useRecoilCallback
。
// atomFamily
const mealsAtom = atomFamily({
key: "meals",
default: {}
});
const mealIds = atom({
key: "mealsIds",
default: []
});
// abstraction
const meals = selectorFamily({
key: "meals-access",
get: (id) => ({ get }) => {
const atom = get(mealsAtom(id));
return atom;
},
set: (id) => ({set}, meal) => {
set(mealsAtom(id), meal);
set(mealIds (id), prev => [...prev, meal.id)]);
}
});
此外,如果您想支持重置,可以使用以下代码:
// atomFamily
const mealsAtom = atomFamily({
key: "meals",
default: {}
});
const mealIds = atom({
key: "mealsIds",
default: []
});
// abstraction
const meals = selectorFamily({
key: "meals-access",
get: (id) => ({ get }) => {
const atom = get(mealsAtom(id));
return atom;
},
set: (id) => ({set, reset}, meal) => {
if(meal instanceof DefaultValue) {
// DefaultValue means reset context
reset(mealsAtom(id));
reset(mealIds (id));
return;
}
set(mealsAtom(id), meal);
set(mealIds (id), prev => [...prev, meal.id)]);
}
});
如果您使用的是 Typescript,您可以使用以下 guard.
使其更优雅import { DefaultValue } from 'recoil';
export const guardRecoilDefaultValue = (
candidate: unknown
): candidate is DefaultValue => {
if (candidate instanceof DefaultValue) return true;
return false;
};
将此守卫与 Typescript 一起使用将类似于:
// atomFamily
const mealsAtom = atomFamily<IMeal, number>({
key: "meals",
default: {}
});
const mealIds = atom<number[]>({
key: "mealsIds",
default: []
});
// abstraction
const meals = selectorFamily<IMeal, number>({
key: "meals-access",
get: (id) => ({ get }) => {
const atom = get(mealsAtom(id));
return atom;
},
set: (id) => ({set, reset}, meal) => {
if (guardRecoilDefaultValue(meal)) {
// DefaultValue means reset context
reset(mealsAtom(id));
reset(mealIds (id));
return;
}
// from this line you got IMeal (not IMeal | DefaultValue)
set(mealsAtom(id), meal);
set(mealIds (id), prev => [...prev, meal.id)]);
}
});
以下是我在当前项目中使用它的方式:
(对于上下文,这是一个从字段选项对象数组创建的动态表单。表单值是通过 graphql 突变提交的,因此我们只希望进行最少的更改。因此,表单构建为用户编辑字段)
import { atom, atomFamily, DefaultValue, selectorFamily } from 'recoil';
type PossibleFormValue = string | null | undefined;
export const fieldStateAtom = atomFamily<PossibleFormValue, string>({
key: 'fieldState',
default: undefined,
});
export const fieldIdsAtom = atom<string[]>({
key: 'fieldIds',
default: [],
});
export const fieldStateSelector = selectorFamily<PossibleFormValue, string>({
key: 'fieldStateSelector',
get: (fieldId) => ({ get }) => get(fieldStateAtom(fieldId)),
set: (fieldId) => ({ set, get }, fieldValue) => {
set(fieldStateAtom(fieldId), fieldValue);
const fieldIds = get(fieldIdsAtom);
if (!fieldIds.includes(fieldId)) {
set(fieldIdsAtom, (prev) => [...prev, fieldId]);
}
},
});
export const formStateSelector = selectorFamily<
Record<string, PossibleFormValue>,
string[]
>({
key: 'formStateSelector',
get: (fieldIds) => ({ get }) => {
return fieldIds.reduce<Record<string, PossibleFormValue>>(
(result, fieldId) => {
const fieldValue = get(fieldStateAtom(fieldId));
return {
...result,
[fieldId]: fieldValue,
};
},
{},
);
},
set: (fieldIds) => ({ get, set, reset }, newValue) => {
if (newValue instanceof DefaultValue) {
reset(fieldIdsAtom);
const fieldIds = get(fieldIdsAtom);
fieldIds.forEach((fieldId) => reset(fieldStateAtom(fieldId)));
} else {
set(fieldIdsAtom, Object.keys(newValue));
fieldIds.forEach((fieldId) => {
set(fieldStateAtom(fieldId), newValue[fieldId]);
});
}
},
});
原子是选择器,在应用程序的 3 个地方使用:
字段组件中:
...
const localValue = useRecoilValue(fieldStateAtom(fieldId));
const setFieldValue = useSetRecoilState(fieldStateSelector(fieldId));
...
在保存处理组件中(尽管在具有显式提交按钮的表单中这可能更简单):
...
const fieldIds = useRecoilValue(fieldIdsAtom);
const formState = useRecoilValue(formStateSelector(fieldIds));
...
在另一个处理表单操作(包括表单重置)的组件中:
...
const resetFormState = useResetRecoilState(formStateSelector([]));
...
const handleDiscard = React.useCallback(() => {
...
resetFormState();
...
}, [..., resetFormState]);
您可以使用 atom
来跟踪 atomFamily
中每个原子的 ID。然后在 atomFamily
中添加或删除新原子时,使用 selectorFamily
或自定义函数用 id 列表更新原子。然后,具有 id 列表的原子可用于通过它们的 id 从 selectorFamily
.
// File for managing state
//Atom Family
export const mealsAtom = atomFamily({
key: "meals",
default: {},
});
//Atom ids list
export const mealsIds = atom({
key: "mealsIds",
default: [],
});
这是 selectorFamily
的样子:
// File for managing state
export const mealsSelector = selectorFamily({
key: "mealsSelector",
get: (mealId) => ({get}) => {
return get(meals(mealId));
},
set: (mealId) => ({set, reset}, newMeal) => {
// if 'newMeal' is an instance of Default value,
// the 'set' method will delete the atom from the atomFamily.
if (newMeal instanceof DefaultValue) {
// reset method deletes the atom from atomFamily. Then update ids list.
reset(mealsAtom(mealId));
set(mealsIds, (prevValue) => prevValue.filter((id) => id !== mealId));
} else {
// creates the atom and update the ids list
set(mealsAtom(mealId), newMeal);
set(mealsIds, (prev) => [...prev, mealId]);
}
},
});
现在,你如何使用所有这些?
- 创建一顿饭:
在这种情况下,我使用当前时间戳作为具有 Math.random()
// Component to consume state
import {mealsSelector} from "your/path";
import {useSetRecoilState} from "recoil";
const setMeal = useSetRecoilState(mealsSelector(Math.random()));
setMeal({
name: "banana",
price: 5,
});
- 删除一顿饭:
// Component to consume state
import {mealsSelector} from "your/path";
import {DefaultValue, useSetRecoilState} from "recoil";
const setMeal = useSetRecoilState(mealsSelector(mealId));
setMeal(new DefaultValue());
- 从 atomFamily 中获取所有原子:
循环 id 列表并呈现接收 id 作为 props 的 Meals 组件,并使用它来获取每个原子的状态。
// Component to consume state, parent of Meals component
import {mealsIds} from "your/path";
import {useRecoilValue} from "recoil";
const mealIdsList = useRecoilValue(mealsIds);
//Inside the return function:
return(
{mealIdsList.slice()
.map((mealId) => (
<MealComponent
key={mealId}
id={mealId}
/>
))}
);
// Meal component to consume state
import {mealsSelector} from "your/path";
import {useRecoilValue} from "recoil";
const meal = useRecoilValue(mealsSelector(props.id));
然后,您有一个餐食组件列表,每个组件都有自己的状态,来自 atomFamily
。