为什么使用useDispatch会重新渲染父组件?

6
我在Ant库中的树组件的onSelect回调函数中使用Redux的useDispatch hook:
export const MyComponent = () => {

    const dispatch = useDispatch();

    const onSelect = (selectedNode) => {
            const selectedItem = selectedNode[0];
            dispatch(fetchSelectedItems(selectedItem));
    };

    return 
        <Tree
            onSelect={onSelect}
        >
            <TreeNode .. />
            <TreeNode .. />
            <TreeNode .. />
        </Tree
}


export const fetchSelectedItems = (selected: string) =>
    (dispatch) =>
        axios({
            url: `/api/items?id=${selected}`,
            method: 'GET',
        }).then(response => {
            dispatch(fetchSelectedItemsSuccess(response.data))
        }).catch((error: any) => {throw(error)});

为什么useDispatch会重新渲染父组件?有没有办法防止这种情况发生?我尝试了Redux文档中使用useCallback的方法,但这种解决方案只能防止子组件重新渲染,而不能防止父组件重新渲染。

2
嗨@kriz,欢迎来到Stack Overflow。你能提供fetchSelectedItems和父组件的代码吗?(因为父组件使用通过fetchSelectedItems检索到的数据,所以父组件将重新渲染)。一个最小可复现示例会很好。 - dance2die
1
即使我执行一些“虚拟”的调度,例如 dispatch({ type: 'SOME_ACTION_NAME' }),这种情况仍然会发生。 - kriz
嘿 @kriz,你能弄清楚这个吗? - Rajat Verma
3个回答

1

看起来我的评论中的假设是正确的。

因此,我将向您展示解决方法。
您可以将容器中使用clickValue的部分提取到另一个组件中,比如ClickValue

这样做将仅隔离对ClickValue组件的更新。

我的分支:https://codesandbox.io/s/soanswer60515755-9cc7u

function ClickValue() {
  const clickValue = useSelector(state => state.value);
  console.log(clickValue);
  return clickValue;
}

export default function Container() {
  return (
    <div className="Container">
      <h3>Container</h3>
      <ParentComponent />
      <ClickValue />
    </div>
  );
}

请查看下面的个人资料结果。

profile result


1
谢谢你的帮助。当你需要渲染一些东西时,这很有用。在我的情况下,我需要传递值,所以最终我使用了 reselect 库重写了我的选择器,现在它们被记忆化了,这解决了我的问题。 - kriz
不用谢,感谢分享答案。您可以将您的解决方案作为回复添加并标记为答案吗? - dance2die

0
我认为在每次渲染时,您都会重新声明onSelect函数。函数是引用类型。在每次渲染时传递具有其新引用的重新声明的函数将导致重新渲染。也许您应该考虑使用上下文。

这会导致父组件重新渲染吗? - Richard Matsen
任何属性的引用更改都会导致重新渲染。 - Jordan Papaleo
2
但不是从子级到父级的。 - user12697177
如果您在组件内声明一个函数,任何导致该父组件进入渲染周期的状态更改都会创建一个新函数。这正是为什么您不能在函数内部执行防抖操作,而必须使用useCallback钩子的原因。 - Jordan Papaleo

0

我的重新渲染组件的问题是由于在父组件中使用useSelector并直接引用state。很可能是因为此选择器的新结果。

解决方案: 我使用reselect库重写了这些选择器,使它们成为记忆化的(在这里的评论中建议过,但我不知道为什么已经被删除)。我完全按照redux文档中关于记忆化选择器的说明进行操作。


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