人人都会AI编程

直接修改 state 的隐患

更新时间:2026-07-10

在 React 中,状态(state)被视为不可变数据。这意味着你不应该直接修改 state 的值,而是要通过 setState 或 useState 的更新函数来返回一个新的值。直接修改 state 是 React 开发中最常见的反模式之一,看似“无害”,实则暗藏多个隐患。

为什么直接修改是危险的?

1. 视图不会更新

React 通过浅比较(Shallow Compare)判断状态是否变化。如果你直接修改了 state 对象的属性,由于引用地址未变,React 会认为状态没有更新,从而跳过重新渲染

// ❌ 错误:直接修改 state
const [user, setUser] = useState({ name: 'Alice', age: 25 });

user.name = 'Bob';
setUser(user); // React 对比发现 user 引用未变,不会重新渲染

正确做法是创建一个新对象:

// ✅ 正确:通过展开运算符创建新对象
setUser({ ...user, name: 'Bob' });

2. 导致不可预测的 Bug

直接修改 state 可能会在异步场景下产生竞态问题。由于 React 的批量更新机制,多次直接修改可能互相覆盖,最终丢失部分更新。

for (let i = 0; i < 3; i++) {
  user.age += 1;           // 直接修改同一个对象
  setUser(user);           // 每次 setUser 引用相同,最终只生效一次
}
// 期望 age 为 28,实际只增加了 1

3. 破坏 React 的不可变数据模型

不可变数据是 React 性能优化(如 React.memo、PureComponent)和 Hooks(如 useEffect 依赖项)的基石。一旦破坏这个约定,依赖浅比较的优化全部失效,甚至导致副作用异常。

useEffect(() => {
  console.log('user changed');
}, [user]); // 如果直接修改 user,依赖项对比始终为 true,副作用不再执行

4. 并发模式下的状态撕裂

React 18 引入并发渲染后,状态更新可能被打断并重试。如果直接修改共享状态,可能读到中间态的不一致数据,引发难以调试的撕裂问题。

常见陷阱与正确做法

数组操作

// ❌ 直接 push,返回原数组引用
const [items, setItems] = useState([]);
items.push(newItem);
setItems(items);

// ✅ 返回新数组
setItems([...items, newItem]);
// 或使用函数式更新(推荐依赖旧状态时使用)
setItems(prev => [...prev, newItem]);

嵌套对象

// ❌ 深层直接修改
const [profile, setProfile] = useState({ info: { name: 'Alice' } });
profile.info.name = 'Bob';
setProfile(profile);

// ✅ 使用展开运算符浅拷贝(对于深层嵌套推荐使用 immer 等库)
setProfile({
  ...profile,
  info: { ...profile.info, name: 'Bob' }
});

删除数组项

// ❌ 直接 splice,修改原数组
const newItems = items;
newItems.splice(index, 1);
setItems(newItems);

// ✅ 使用 filter 返回新数组
setItems(items.filter((_, i) => i !== index));

如何彻底避免?

  1. 始终使用扩展运算符或 map/filter/concat 等创建新引用
  2. 使用函数式更新:当新状态依赖旧状态时,使用 setState(prev => newValue)
  3. 复杂状态使用 useReducer:Reducer 的不可变逻辑更清晰。
  4. 引入 Immer 简化不可变操作:通过 produce 函数以“可变”语法生成不可变数据。
  5. 开启 ESLint 规则:如 react/no-direct-mutation-state 能在开发阶段捕获这类错误。

直接修改 state 是一个很容易犯的错,尤其是在数据嵌套较深时。但理解其背后的不可变原理,并养成对应的编码习惯,就能从根本上避免这个隐患,写出更可靠、更易维护的 React 代码。