React 在使用扩展运算符更改状态时渲染,但在传递数组本身时不渲染 [重复]

Posted

技术标签:

【中文标题】React 在使用扩展运算符更改状态时渲染,但在传递数组本身时不渲染 [重复]【英文标题】:React renders when changing state using spread operator, but not when passing the array itself [duplicate] 【发布时间】:2021-09-07 10:17:33 【问题描述】:

我遇到了一个问题,即通过传入数组更改状态后 React 无法呈现,尽管状态已成功更新:

const [phoneNumber, setPhoneNumber] = useState([])
...
const newNumber = phoneNumber
newNumber.pop()
setPhoneNumber(newNumber)

但是,在使用扩展运算符传递 newNumber 后,应用程序按预期重新呈现:

const newNumber = phoneNumber
newNumber.pop()
setPhoneNumber([...newNumber])

是什么原因造成的?

【问题讨论】:

随着传播,您可以克隆对象(在本例中为数组),因此 react 将获得一个全新的引用,而在第一种情况下,引用永远不会改变 【参考方案1】:

您将newNumber 设置为等于phoneNumber。这意味着newNumberphoneNumber引用。当您弹出newNumber 时,您正在弹出phoneNumber。然后,您实际上将状态设置回phoneNumber。尽管值已更改,但它仍然是相同的引用,因此 react 假定它不需要重新渲染。

当您传播数组时,您正在创建一个 数组(在内存中有一个新的引用),因此 react 会重新渲染组件。

这是一种反模式,你不应该像这样直接改变状态值phoneNumber。相反,您应该创建一个新值(例如,当您传播它时)并使用您的新值更新状态。

【讨论】:

明白。我解决这个问题的方法是设置: newNumber = [...phoneNumber] newNumber.pop() setPhoneNumber(newNumber) 这似乎有点麻烦。有没有更好的方法来做到这一点? 嗯,你可以setPhoneNumber(phoneNumber.slice(0, phoneNumber.length - 1)),但也有点麻烦。我通常使用lodash 库来进行诸如此类的繁琐数据操作。它们对一切都有作用。在这种情况下,您可以使用 dropRight lodash.com/docs/4.17.15#dropRight【参考方案2】:

反应docs:

永远不要直接改变 this.state,因为之后调用 setState() 可能 替换您所做的突变。把 this.state 当作是 不可变。

setState() 不会立即改变 this.state 而是创建一个 等待状态转换。调用 this 后访问 this.state 方法可能会返回现有值

不保证 setState 调用的同步操作 并且调用可能会被批处理以提高性能。

setState() 将始终触发 重新渲染,除非有条件 渲染逻辑在 shouldComponentUpdate() 中实现。如果可变 正在使用对象,并且无法在其中实现逻辑 shouldComponentUpdate(),只有在新状态时才调用 setState() 与之前的状态不同将避免不必要的重新渲染。

通过不可变数据结构来判断任何道具或状态(如数组、对象等)何时自动更改的有效方法。

使用不可变数据结构背后的想法很简单。对于复杂的数据类型,比较在其参考上执行。每当包含复杂数据的对象发生更改时,我们可以创建该对象的副本,而不是在该对象中进行更改,这将创建一个新的引用ES6 具有 对象传播运算符 来实现这一点。

有关可变状态问题的更多信息,请阅读this。

【讨论】:

以上是关于React 在使用扩展运算符更改状态时渲染,但在传递数组本身时不渲染 [重复]的主要内容,如果未能解决你的问题,请参考以下文章

React/Nextjs 上下文提供程序状态在第一次加载/刷新时无法访问,但在状态更改时工作正常

React 功能组件在重新挂载后停止渲染状态更改

状态更改时不会重新渲染 React-typing-animation

为使用React的网站制作Chrome扩展程序。重新渲染后如何保持更改?

React(性能):如何防止每次状态更改时出现不必要的渲染? [复制]

当一项更改时,React 会重新渲染整个列表