React 纠错指南:直接改 state 而不是用 setState
·
React 纠错指南:直接改 state 而不是用 setState
—— 从“页面不更新”到“数据被覆盖”一次说清
一、为什么必须 setState?
React 的核心机制:
-
状态是不可变的
this.state/useState返回的是快照,直接改对象不会触发重新渲染。 -
批处理与调和
只有setState/setXxx会把变更加入队列,统一批量更新 DOM。 -
时间旅行与调试
直接突变会让 DevTools、Redux、水合检测全部失效。
二、3 个高频翻车现场
| 场景 | 错误写法 | 现象 |
|---|---|---|
| 类组件 | this.state.list.push(item) | 页面不更新 |
| 函数组件 | arr[0] = 1 | 下一次 setState 把旧值覆盖回来 |
| 嵌套对象 | obj.a.b = newVal | 调试工具看不到变化 |
三、正确 vs 错误对比(可copy)
1️⃣ 类组件
// ❌ 直接突变
this.state.list.push(item);
// ✅ immutable 更新
this.setState(prev => ({
list: [...prev.list, item]
}));
2️⃣ 函数组件
// ❌ 直接改数组
const [list, setList] = useState([]);
list.push(item); // 不触发渲染
setList(list); // 仍指向同一引用,React 跳过更新
// ✅ 生成新引用
setList(prev => [...prev, item]);
3️⃣ 深层对象
// ❌ 直接改嵌套属性
const [user, setUser] = useState({ profile: { name: '' } });
user.profile.name = 'Tom'; // 突变
setUser(user); // 引用未变,不更新
// ✅ 逐层展开
setUser(prev => ({
...prev,
profile: { ...prev.profile, name: 'Tom' }
}));
四、批量更新陷阱
// ❌ 三次直接修改,只会触发一次渲染,且数据被覆盖
const [count, setCount] = useState(0);
count++; // 0 → 1
count++; // 仍 1(引用未变)
setCount(count); // 最终 1
// ✅ 函数式更新,每次基于最新状态
setCount(c => c + 1);
setCount(c => c + 1);
// 最终 2
五、Immutable 工具链(可选)
| 工具 | 示例 |
|---|---|
| Immer | produce(draft => { draft.list.push(item) }) |
| use-immer | const [state, update] = useImmer(initial) |
| lodash cloneDeep | setState(_.cloneDeep(obj)) |
六、一句话总结
永远把 state 当成只读快照。
任何变更都通过setState/setXxx并返回新引用,React 才会“听到”你的声音。
最后问候亲爱的朋友们,并邀请你们阅读我的全新著作
📚 《 React开发实践:掌握Redux与Hooks应用 》

更多推荐
所有评论(0)