React 浅拷贝仍然会触发重新渲染?

React shallow copy still triggers re-render?

根据我对 React 的了解,你不应该改变任何对象,否则 React 不知道重新渲染,例如,下面的例子不应该在 UI 按钮时触发重新渲染被点击:

import React, { useState } from "react";
import ReactDOM from "react-dom";

function App({ input }) {
  const [items, setItems] = useState(input);

  return (
    <div>
      {items.map((item) => (
        <MyItem item={item}/>
      ))}
      <button
        onClick={() => {
          setItems((prevItems) => {
            return prevItems.map((item) => {
              if (item.id === 2) {
                item.name = Math.random();
              }
              return item;
            });
          });
        }}
      >
        Update wouldn't work due to shallow copy
      </button>
    </div>
  );
}

function MyItem ({item}) {
  const name = item.name
  return <p>{name}</p>
}

ReactDOM.render(
  <App
    input={[
      { name: "apple", id: 1 },
      { name: "banana", id: 2 }
    ]}
  />,
  document.getElementById("container")
);

你可以试试上面的代码here

更新对象数组的正确方法应该如下所示(其他深度复制方法也可以)

 setItems((prevItems) => {
    return prevItems.map((item) => {
               if (item.id === 2) {
                   # This way we return a deepcopy of item
                   return {...item, name: Math.random()}
               }
               return item;
            });
});

为什么第一个版本工作正常并且 UI 立即更新,即使我只是更新原始项目对象?

渲染是由于 .map 创建了新数组。如果您在挂钩中执行类似 prev[1].name = "x"; return prev; 的操作,则不会执行更新。 Per reactjs doc on setState with function argument:

If your update function returns the exact same value as the current state, the subsequent rerender will be skipped completely.

更新.

是的,说到 parent-child 交互,item 是相同的(参考),但 child props 会有所不同。你有 MyItem({ item }) 并且这个 item 正在从 props 解构,比如 MyItem(props),并且这个 props 改变是因为 parent 源改变了.

所以每次你 map 列表时,你明确地要求 parent 呈现它的 children,并且 [=41] 的一些(或全部)的事实=]的param没改也没关系。为了证明这一点,您可以从 child 组件中删除任何参数:

  {items.map(() => ( <MyItem /> ))}
function MyItem () {
  return <p>hello</p>
}

MyItem 将在您每次通过状态挂钩执行 items 更新时调用。而且它的 props 将始终与以前的版本不同。

如果您的 setItem 设置为 new object,您的页面状态更改将重新呈现 按照您的逻辑,您执行了一个浅拷贝,其中:

  • 浅拷贝创建了一个新对象,并从旧对象复制了第一层的所有内容。

浅拷贝和深拷贝也创建了一个新的对象,它们都触发了React中的重新渲染。

浅拷贝和深拷贝的区别在于:从旧对象的第2层开始,浅拷贝保持相同的对象,而深拷贝会在所有层创建新的对象。