为啥 useDispatch 重新渲染父组件?

Posted

技术标签:

【中文标题】为啥 useDispatch 重新渲染父组件?【英文标题】:Why useDispatch re-renders parent components?为什么 useDispatch 重新渲染父组件? 【发布时间】:2020-06-16 08:05:58 【问题描述】:

我在 Tree 组件(来自 Ant 库)的 onSelect 回调中使用 useDispatch 挂钩(来自 Redux):

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 会重新渲染父组件?有什么办法可以防止这种情况发生吗?我尝试了 useCallback 就像它在 Redux 文档中一样,但 this solution 是为了防止子组件重新渲染,而不是父组件。

【问题讨论】:

嗨@kriz 欢迎来到堆栈溢出。你能提供fetchSelectedItems和父母的代码吗? (因为父级使用通过fetchSelectedItems 检索到的数据,所以父级会重新渲染)。 minimal reproducible example 会很棒。 即使我做了一些像dispatch( type: 'SOME_ACTION_NAME' )这样的“虚拟”调度,也会发生这种情况 【参考方案1】:

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

【讨论】:

这会导致父级重新渲染吗? 对任何地方的任何道具的任何引用更改都会导致重新渲染 但不是从孩子到父母。 如果您在组件中声明一个函数,任何导致该父级进入渲染周期的状态更改都会生成一个新函数。这就是为什么你不能在函数中进行去抖动并且你需要使用 useCallback 钩子的原因。【参考方案2】:

看来我在the comment 中的假设是正确的。

所以我会告诉你解决方法。 您可以将容器中使用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>
  );

查看下面的配置文件结果。

【讨论】:

谢谢。当您需要渲染某些东西时,它会有所帮助。在我的情况下,我需要传递值,所以最后我使用 reselect 库重写了我的选择器,现在它们被 momoized 这解决了我的问题。 不客气,感谢您分享答案。您能否将您的解决方案添加为回复并将其标记为答案?【参考方案3】:

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

解决方案: 我用reselect library 重写了这个选择器以使它们被记忆(这是在这里的一个 cmets 中提出的,但我不知道为什么它被删除了)。我所做的正是 redux 文档中关于 memoized selectors 的内容。

【讨论】:

以上是关于为啥 useDispatch 重新渲染父组件?的主要内容,如果未能解决你的问题,请参考以下文章

子组件不会重新渲染,但父组件会重新渲染。如何让子组件重新渲染?

useDispatch 渲染未更改的子级

如何在反应原生的子组件上重新渲染父组件?

React Native - 从父组件重新渲染子组件

反应父组件不重新渲染

重新渲染 Reactjs 后,父组件立即失去状态