调用React的set状态函数时,React会对传入的对象做克隆处理吗?
问题解答
核心结论
- 你写的用法不安全,修改已经传给setUser的克隆对象,本质就是直接修改当前状态
- React不会对你传入set函数的对象做任何二次克隆,会直接存储该对象的引用作为新状态
- 不需要每次都额外深克隆,只要保证每次传给set函数的都是一个全新的、没有被后续修改的对象即可
具体逻辑说明
你第一次执行setUser(newUser)之后,React内部存储的user状态就和你手里的newUser变量指向了同一个内存对象。此时你修改newUser.email,等于直接修改了当前的user状态值,这违反了React状态不可变的原则,可能会引发两个问题:
- 状态会在你意料之外的时机被修改,可能引发渲染逻辑异常
- 你后续调用
setUser(newUser)时,React的默认浅比较会判定新旧状态是同一个引用,直接跳过这次更新,导致视图不会刷新最新的email值
正确写法推荐
方案1:每次更新都生成新的对象
const [user, setUser] = useState({ name: '', email: '' }) // 第一次更新 const newUser = cloneDeep(user) newUser.name = 'Steve' setUser(newUser) // 这里用完就不要再操作newUser了 // 异步操作后第二次更新 const updatedUser = cloneDeep(user) // 重新克隆最新的状态 updatedUser.email = 'steve@example.com' setUser(updatedUser)
方案2:用函数式更新避免依赖外部状态引用(更推荐)
如果异步操作间隔时间较长,可能存在状态已经被其他逻辑修改的情况,用函数式更新可以直接拿到最新的状态值,更稳妥:
const [user, setUser] = useState({ name: '', email: '' }) // 第一次更新 setUser(prev => { const newUser = cloneDeep(prev) newUser.name = 'Steve' return newUser }) // 异步操作后第二次更新 setUser(prev => { const updatedUser = cloneDeep(prev) updatedUser.email = 'steve@example.com' return updatedUser })
内容的提问来源于stack exchange,提问作者Glenn
相关产品推荐
相关产品推荐

