【问题标题】: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' })这样的“虚拟”调度,也会发生这种情况

标签: javascript reactjs redux react-hooks


【解决方案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 的内容。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-08-12
    • 2019-06-01
    • 1970-01-01
    • 2020-02-13
    • 1970-01-01
    • 2011-03-01
    • 2021-11-24
    • 2021-12-01
    相关资源
    最近更新 更多