【问题标题】:React shallow copy still triggers re-render?React 浅拷贝仍然触发重新渲染?
【发布时间】:2021-02-17 01:16:21
【问题描述】:

根据我对 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 也会立即更新?

【问题讨论】:

    标签: javascript reactjs setstate


    【解决方案1】:

    由于.map 创建了新数组而发生渲染。如果您在挂钩中执行prev[1].name = "x"; return prev; 之类的操作,则不会执行更新。根据setState 上的reactjs doc 和函数参数:

    如果您的更新函数返回与当前函数完全相同的值 状态,后续的重新渲染将被完全跳过。

    更新

    是的,说到父子交互,item 将是相同的(通过引用),但子 props 会有所不同。你有MyItem({ item }),而这个item 正在从props 解构,比如MyItem(props),并且这个props 会因为父源的变化而发生变化。

    因此,每次map 列表时,您都明确要求父级渲染其子级,而子级的部分(或全部)参数未更改这一事实并不重要。为了证明这一点,您可以从子组件中删除任何参数:

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

    每次您通过状态挂钩执行items 更新时,都会调用MyItem。而且它的props总是和之前的版本不一样。

    【讨论】:

    • 嗯,我知道 map 返回一个新数组,但这足以触发所有子组件的重新渲染?为什么我读了很多关于深度复制数组中每个对象的帖子也是必需的?例如 - stackoverflow.com/questions/28121272/…
    • @Arch1tect 请看一下,考虑到亲子问题,我扩展了答案。另外,我认为,您可能需要更新代码框演示,因为它目前不包含子组件。
    【解决方案2】:

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

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

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

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

    【讨论】:

      猜你喜欢
      • 2011-02-09
      • 2012-04-12
      • 2017-10-14
      • 1970-01-01
      • 2015-01-13
      • 2011-09-05
      • 1970-01-01
      • 1970-01-01
      • 2016-05-07
      相关资源
      最近更新 更多