首页 > 解决方案 > 当我使用选择器正常功能时,ReactJs 状态正在循环中触发

问题描述

主要问题:我尝试在选择器中使用一个函数来重新构造数据并加入另一个变量,在这种情况下,我的组和他们的孩子作为项目放在一起,问题是每次都会在无限循环中调用该函数,尽管状态没有被改变。

我有这个选择器: const groups = useSelector(state => selectProductGroups(state));

功能是这样的:

  const groups = state.PlatformsReducer.groups;
  const items = state.PlatformsReducer.items;
  return groups.reduce((ac, g) => {
    g.items = items.filter(i => i.groupId == g.productNumber);
    if (ac[g.platformId]) {
      ac[g.platformId].push(g);
    } else {
      ac[g.platformId] = [g];
    }
    return ac;
  }, {});
};

So when I use a useEffect to detect if the groups variable has changed the useEffect is triggered in a loop despite the variable groups still empty.

Do you know why? or How to prevent this.

I now the problem is the function in the selector, but I don't know how to prevent this case.

标签: javascriptreactjsuse-effectredux-selector

解决方案


这与useSelector钩子在内部的作用有关。

useSelector运行您的选择器并检查结果是否与先前收到的结果相同(参考比较)。如果结果不同,则存储新结果并强制重新渲染。如果结果相同,则不会替换旧结果,也不会触发重新渲染。

这确实意味着每次存储更新时,即使它是状态的不相关部分,也会运行您的复杂函数来确定结果是否发生了变化。在你的情况下,它总是一个新的参考,因此总是一个变化。

我认为处理这个问题的最好方法是让你的选择器尽可能简单,或者使用某种形式的更复杂的记忆,比如 reselect 提供的。

下面是一个示例,说明您如何能够使选择器保持简单,但仍然可以使用自定义挂钩实现一种简单的方法来重用您的产品组选择。

const useProductGroups = () => {
    // Get groups from the store. 
    // As the selector does not create a new object it should only 
    // trigger a rerender when groups changes in the store.
    const groups = useSelector(state => state.PlatformsReducer.groups);

    // Get items from the store, 
    // As the selector does not create a new object it should only 
    // trigger a rerender when items changes in the store.
    const items = useSelector(state => state.PlatformsReducer.items);

    // Reduce the group collection as desired inside of a useMemo 
    // so that the reduction only occurs when either items or groups 
    // changes.
    const productGroups = useMemo(() => {
        return groups.reduce((ac, g) => {
            g.items = items.filter(i => i.groupId == g.productNumber);

            if (ac[g.platformId]) {
                ac[g.platformId].push(g);
            } else {
                ac[g.platformId] = [g];
            }

            return ac;
        }, {});
    }, [groups, items] /* dependency array on items / groups */);

    // return the calculated product groups
    return productGroups;
}

然后,您可以在函数组件中使用自定义挂钩:

const groups = useProductGroups();

推荐阅读