You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在React中调用setState()修改集合时返回空对象是否可行?

关于React状态修改:为什么直接修改原集合也能运行?

嘿,这个疑惑太正常了!我刚学React的时候也踩过类似的坑——明明文档和各路问答都强调要返回新状态,可自己直接改原数组/对象再塞回setState里,居然也能正常渲染,简直怀疑是不是自己记错了规则😂

先说说为什么你的代码现在能跑

数组和对象都是引用类型,你直接修改原状态里的集合(比如push新待办、splice删除项),虽然没有创建新的引用,但在一些简单场景下,React还是会触发重新渲染:

  • 你的组件是基础的函数组件,没有用React.memo、useMemo这类性能优化,React会默认每次执行组件函数,自然就会读取到修改后的状态
  • 状态更新是同步执行的,React在同步更新流程里可能会做额外的检查,“意外”发现了状态内容的变化(但这不是React设计时依赖的机制)
  • 开发模式下的React有一些容错逻辑,但生产环境可不会这么“宽容”

但为什么文档和StackOverflow都坚决反对这种写法?

React的状态更新是基于引用变化来判断是否需要重新渲染的。如果直接修改原状态:

  1. 可能导致组件不更新:比如在异步操作(setTimeout、API请求回调)里修改原状态,或者组件用了React.memo,React会因为状态引用没变化,判定组件不需要重新渲染,界面就会和实际状态脱节
  2. 调试噩梦:状态是可变的,你没法通过React DevTools清晰追踪状态的变化历史——原状态被直接修改,之前的状态快照都被覆盖了
  3. 违反不可变原则:React的状态设计理念是“不可变”,所有状态更新都应该生成新的状态副本,这样才能保证组件渲染的一致性和可预测性

举个例子对比两种写法

不规范的写法(现在能跑,但隐患大)

const [todos, setTodos] = useState([]);

const addTodo = (text) => {
  // 直接修改原数组
  todos.push({ id: Date.now(), text });
  // 把原引用塞回setState
  setTodos(todos);
};

规范的写法(遵循不可变原则,安全可靠)

const [todos, setTodos] = useState([]);

const addTodo = (text) => {
  // 基于原状态创建新数组副本
  setTodos(prevTodos => [...prevTodos, { id: Date.now(), text }]);
};

// 修改待办状态的正确写法
const toggleTodo = (todoId) => {
  setTodos(prevTodos => 
    prevTodos.map(todo => 
      todo.id === todoId ? { ...todo, completed: !todo.completed } : todo
    )
  );
};

总结

现在的代码能运行只是“巧合”,随着应用规模变大、引入更多复杂逻辑(比如异步操作、性能优化),直接修改原状态的写法一定会出现难以排查的bug。遵循React的不可变状态原则,虽然看起来多写了几行代码,但能保证应用的稳定性和可维护性,绝对是值得的!

内容的提问来源于stack exchange,提问作者Brian H.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 12:12:12