首页 > 解决方案 > React - 过滤存储中的对象会导致递归/无限循环

问题描述

我在我的 react 应用程序中使用 redux store 来存储我的费用等数据。

现在我在其中一个组件中使用这些费用来计算 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 的无限循环。为什么会这样?

标签: javascriptreactjsuse-effect

解决方案


与大多数涉及 React 钩子的事情一样,所涉及的对象的身份(例如,您使用 测试的内容===)在这里很重要。当 的标识expenses与上次渲染不同时,将触发您的效果。


选择器 A

这个钩子返回你的 Redux 状态itemsexpenses属性。如果状态不改变,items财产的身份也不会改变。

const expenses = useSelector(({ expenses }: RootState) => expenses.items);

选择器 B

这将返回调用filteritems 数组的结果。filter 方法返回一个全新的数组。

const expenses = useSelector(({ expenses }: RootState) => 
     expenses.items.filter((expense: Expense) => !expense.deleted)
);

您正在观察的是 Selector B 每次组件呈现时都会返回一个新值,因此效果被触发得太频繁了。

现在 -如果状态没有改变,但只有当你传递给它的选择器函数的标识保持不变时,useSelector 可以缓存它的返回值以重用。您可以通过在模块范围内命名选择器而不是组件来确保这一点。

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 />;
};

推荐阅读