react 更新状态中的对象
State 可以保存任何类型的 JavaScript 值,包括对象。但是你不应该直接改变你在 React 状态下持有的对象。相反,当你想更新一个对象时,你需要创建一个新对象(或复制一个现有对象),然后设置状态以使用该副本。
const [position, setPosition] = useState({ x: 0, y: 0 });
从技术上讲,可以更改对象本身的内容。这称为突变:
position.x = 5;
然而,尽管 React 状态中的对象在技术上是可变的,但您应该将它们视为不可变的——就像数字、布尔值和字符串一样。您应该始终替换它们,而不是改变它们。
const [position, setPosition] = useState({ x: 0, y: 0 });
这样的代码是一个问题,因为它修改了状态中的现有对象:
position.x = e.clientX;
position.y = e.clientY;
但是这样的代码绝对没问题,因为您正在改变刚刚创建的新对象:
const nextPosition = {}; nextPosition.x = e.clientX; nextPosition.y = e.clientY; setPosition(nextPosition);
事实上,它完全等同于这样写:
setPosition({
x: e.clientX,
y: e.clientY
});
只有当您更改已处于状态的现有对象时,变异才会成为问题。改变您刚刚创建的对象是可以的,因为还没有其他代码引用它。更改它不会意外影响依赖它的东西。这被称为“局部突变”。您甚至可以在渲染时进行局部突变。很方便,完全没问题!
我们常用的是使用传播语法复制对象,您可以使用... 对象传播语法,这样您就不需要单独复制每个属性。
setPerson({ ...person, // Copy the old fields firstName: e.target.value // But override this one });
用 Immer 编写简洁的更新逻辑
如果您的状态嵌套很深,您可能需要考虑将其展平。但是,如果您不想更改状态结构,您可能更喜欢嵌套传播的捷径。Immer是一个流行的库,它允许您使用方便但可变的语法进行编写,并负责为您生成副本。使用 Immer,您编写的代码看起来像是在“违反规则”并改变对象:
updatePerson(draft => { draft.artwork.city = 'Lagos'; });
但与常规突变不同的是,它不会覆盖过去的状态!
尝试 Immer:
运行npm install use-immer以将 Immer 添加为依赖项
然后替换 import { useState } from 'react'为import { useImmer } from 'use-immer'
例如:
import { useImmer } from 'use-immer';
export default function Form() {
const [person, updatePerson] = useImmer({
name: 'Niki de Saint Phalle',
artwork: {
title: 'Blue Nana',
city: 'Hamburg',
image: 'https://i.imgur.com/Sd1AgUOm.jpg',
}
});
function handleNameChange(e) {
updatePerson(draft => {
draft.name = e.target.value;
});
}
function handleTitleChange(e) {
updatePerson(draft => {
draft.artwork.title = e.target.value;
});
}
function handleCityChange(e) {
updatePerson(draft => {
draft.artwork.city = e.target.value;
});
}
function handleImageChange(e) {
updatePerson(draft => {
draft.artwork.image = e.target.value;
});
}
return (
<>
<label>
Name:
<input
value={person.name}
onChange={handleNameChange}
/>
</label>
<label>
Title:
<input
value={person.artwork.title}
onChange={handleTitleChange}
/>
</label>
<label>
City:
<input
value={person.artwork.city}
onChange={handleCityChange}
/>
</label>
<label>
Image:
<input
value={person.artwork.image}
onChange={handleImageChange}
/>
</label>
<p>
<i>{person.artwork.title}</i>
{' by '}
{person.name}
<br />
(located in {person.artwork.city})
</p>
<img
src={person.artwork.image}
alt={person.artwork.title}
/>
</>
);
}
请注意事件处理程序变得多么简洁。您可以根据需要在单个组件中混合搭配useState。useImmerImmer 是保持更新处理程序简洁的好方法,尤其是当您的状态中存在嵌套并且复制对象会导致重复代码时。

浙公网安备 33010602011771号