使用 React hooks 和 memo 时如何防止子组件重新渲染?

How to prevent child component from re-rendering when using React hooks and memo?

我刚开始尝试使用 React 挂钩,我想知道如何防止子组件在父组件重新呈现时重新呈现。我正在寻找类似于在 componentDidUpdate 中返回 false 的内容。我的问题似乎源于我在子组件中调用以更改父组件状态的点击处理程序。由于该函数是在父组件中创建的,因此在每个父渲染上都会创建新的,这会触发子组件中的道具更改,然后导致子组件重新渲染(我认为).下面是一些示例代码来帮助说明情况。

function Parent() {
    const [item, setItem] = useState({ name: "item", value: 0 });

    const handleChangeItem = () => {
        const newValue = item.value + 1;
        setItem({ ...item, value: newValue });
    };

    return <Child item={item} changeItem={handleChangeItem} />;
}

const Child = React.memo(function Child({ item, changeItem }) {
    function handleClick(){
        changeItem();
    }
    return (
        <div>
            Name: {item.name} Value: {item.value}
            <button onClick={handleClick}>change state in parent</button>
        </div>
    );
});

如何防止每次父组件渲染时都渲染子组件?父级中的 handleChangeItem 是否应该存在于其他地方,以便它不会在每次渲染时重新创建?如果是这样,它如何访问 useState 返回的 itemsetItem

我对反应还很陌生,刚开始玩钩子,所以我可能遗漏了一些明显的东西。

在您的情况下,记忆 Child 没有任何意义,因为如果项目发生变化,child 必须 re-render。但是,如果存在 props 没有改变的情况,但由于重新创建的函数,child 仍然是 re-rendering,您将使用 useCallback 挂钩来记忆函数,在每个渲染。此外,由于您已经记忆了处理程序,因此您应该使用回调方法来更新状态,因为处理程序内部的 item 将仅引用最初创建函数时它所具有的值

function Parent() {
  const [item, setItem] = useState({ name: "item", value: 0 });

  const handleChangeItem = useCallback(() => {
    setItem(prevItem => ({ ...prevItem, value: prevItem.value + 1 }));
  }, []);

  return (
    <>
      Name: {item.name} Value: {item.value}
      <Child changeItem={handleChangeItem} />
    </>
  );
}

const Child = React.memo(function Child({ item, changeItem }) {
  function handleClick() {
    changeItem();
  }
  console.log("child render");
  return (
    <div>
      <button onClick={handleClick}>change state in parent</button>
    </div>
  );
});

Working demo

P.S. Credit to @danAbramov for the direction

Shubham Khatri 准确地回答了原始问题,但我添加此答案是为了指出避免此回调问题的推荐方法。

来自文档:

useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values. It also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.

https://reactjs.org/docs/hooks-reference.html#usereducer

来自常见问题解答:

In large component trees, an alternative we recommend is to pass down a dispatch function from useReducer via context...

https://reactjs.org/docs/hooks-faq.html#how-to-avoid-passing-callbacks-down

所有这一切的关键是分派永远不会改变,这与每次渲染都创建的回调不同。