React - 在store中过滤对象会导致递归/无限循环

3

我在我的React应用程序中使用Redux存储来存储数据,例如我的开销。

现在我在其中一个组件中使用这些开销来计算待办事项徽标计数,如下所示:

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

这导致了useEffect被不断地调用。为什么会发生这种情况?
1个回答

2
与大多数涉及React hooks的事情一样,对象的身份(例如您使用 === 测试的内容)在这里非常重要。当expenses的身份与上次渲染不同时,会触发您的effect。

选择器A

此hook返回Redux状态中expenses属性的items属性。如果状态不改变,则items属性的身份也不会改变。

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

选择器 B

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

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

你所观察到的是选择器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 />;
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接