反应浅拷贝仍然触发重新渲染?



根据我对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")
);

你可以试试上面的代码

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

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;之类的操作,则不会执行更新。根据reactjs文档对setState的函数参数:

如果您的更新函数返回与当前相同的值状态时,将完全跳过后续呈现。

更新

是的,说到亲子互动,item是一样的(参考),但孩子props不同。你有MyItem({ item }),这个item是从props中析构出来的,比如MyItem(props),这个props因为父源的改变而改变。

因此,每次您map列表时,您都显式地要求父节点呈现其子节点,并且子节点的一些(或全部)参数没有改变的事实并不重要。为了证明这一点,你可以从子组件中删除任何参数:

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

MyItem将在每次通过状态钩子执行items更新时被调用。并且它的props将永远不同于以前的版本。

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

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

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

浅拷贝和深拷贝的不同之处在于:从旧对象的第2层开始,浅拷贝保持相同的对象,深拷贝将在所有层创建新对象。

最新更新