React 突变状态
刚开始开发时,我们可能会遇到这样的问题,使用useState创建对象或者数组时,setState时,页面未进行更新。这里其实是一个突变状态的问题
-
什么是突变状态?
当我们给
setState
一个基本数据类型时,state
值将会是一个不可变的值 更新时,
state
的原始值也不会被更改,而是重新创建一个不可变的的基本数据类型,以触发重新渲染 -
为什么React不推荐突变状态?
-
调试:如果你使用
console.log
并且不改变状态,你过去的日志将不会被最近的状态破坏修改,你可以清楚的看到渲染之间的状态变化 -
优化:如果之前的
props
和state
和下一个状态相同,常见的react
优化策略将会跳过本次渲染,如果你从不改变状态,检查变化就会非常的快,如果prevProps === props
,react就可以确定它内部并没有发生变化 -
新功能:react正在构建的新功能依赖将状态视为快照,如果你正在更新过去的状态版本,这会导致无法使用新功能
-
需求变更:一些需要撤销/重做和显示历史记录的值,在没有突变的情况下更容易执行,这是因为你可以将过去的值保存在副本中,并在适用的情况下重做他们
-
更简单的实现:因为
react
不依赖突变,所以它不需要对你的对象做任何处理,不需要劫持你的对象。总是将它们包装到代理中,或者在初始化时像许多“反应式”解决方案那样做其他工作。这也是为什么react
允许您将任何对象置于状态(无论有多大)而没有额外的性能或正确性陷阱。
-
-
如何更合理的更新对象或者数组
看这样一段代码:
import React from "react"; import ShoppingList from './ShoppingList'; import NewItemForm from './NewItemForm'; function App({ const [items,setItems] = React.usestateC['apple','banana']); function handleAddItem(value) { items.push(value); setItems(items); } return ( <div> {items.length > 0 && <ShoppingList items={items}/>} <NewItemForm handleAddItem={handleAddItem}/> </div> ) } export default App;
每当增加一个新项目时,
handleAddItem
函数就会被调用。但是,它并不起作用!当我们输入一个项目并提交表单时,该项目没有被添加到购物清单中。 问题就在于我们违反了
React
中最核心的原则 —— 不可变状态。React依靠一个状态变量的地址来判断状态是否发生了变化。当我们把一个项目推入一个数组时,我们并没有改变该数组的地址,所以React
无法判断该值已经改变。从技术上讲可以改变对象或者数组本身的内容,我们将其称之为突变,然而,react
的状态在技术上是可变的,但react
仍然推荐使用不可变的方式去改变react
的状态。像基本数据类型一样,去始终替换他们,而不是去改变他们正确的做法:
function handleAddItem(value) { const nextItems = [...items, value]; setItems(nextItems); }
不建议修改一个现有的数组,而是从头开始创建一个新的数组。这里的区别在于编辑一个现有的数组和创建一个新的数组之间的区别。
同样的,对于对象类型的数据也是:
// ❌ 不建议 function handleChangeEmail(nextEmail) { user.email = nextEmail; setUser(user); } // ✅ 推荐 function handleChangeEmail(email) { const nextUser = { ...user, email: nextEmail }; setUser(nextUser); }