我在我的React应用程序中使用redux存储来存储诸如费用之类的数据。
现在,我在这些组件之一中使用这些费用来计算要显示的TODO徽章计数
const [badgeValue, setBadgeValue] = useState(0);
const expenses = useSelector(({ expenses }: RootState) => expenses.items);
useEffect(() => {
const setBadge = async () => {
const badgeNumber = await calculateRequestBadgeNumber(expenses);
setBadgeValue(badgeNumber);
};
setBadge();
return () => setBadgeValue(0);
}, [expenses]);
这按预期工作。现在,当我想像这样过滤掉已删除的费用时,我遇到了一个怪异的副作用:
const expenses = useSelector(({ expenses }: RootState) =>
expenses.items.filter((expense: Expense) => !expense.deleted)
);
这导致useEffects的无限循环。为什么会这样?
答案 0 :(得分:0)
与涉及React钩子的大多数事物一样,所涉及对象的标识(例如,用===
测试的对象)在这里很重要。当expenses
的身份与上次渲染不同时,将触发您的效果。
此钩子返回Redux状态的items
属性的expenses
属性。如果状态不变,则items
属性的身份也不会改变。
const expenses = useSelector(({ expenses }: RootState) => expenses.items);
这将返回在items数组上调用filter
的结果。 filter方法返回一个全新的数组。
const expenses = useSelector(({ expenses }: RootState) =>
expenses.items.filter((expense: Expense) => !expense.deleted)
);
您正在观察的是选择器B每次在组件渲染时都返回一个新值,因此效果触发得太频繁了。
现在-useSelector
can cache its return value,如果状态未更改,则重用,但如果传递给它的选择器函数的身份保持不变,则仅 。您可以通过在模块作用域(而不是组件)中命名选择器来确保这一点。
const filteredExpenses = ({ expenses }: RootState) =>
expenses.items.filter((expense: Expense) => !expense.deleted);
const MyComponent = () => {
const [badgeValue, setBadgeValue] = useState(0);
const expenses = useSelector(filteredExpenses);
useEffect(() => {
const setBadge = async () => {
const badgeNumber = await calculateRequestBadgeNumber(expenses);
setBadgeValue(badgeNumber);
};
setBadge();
return () => setBadgeValue(0);
}, [expenses]);
return <StuffToRender />;
};