1. 异步更新导致取不到旧值
- 坑:useState的更新是异步批量处理的,如果直接使用旧值计算新值,可能会出错。
jsx
const [count,setCount] = useState(0)
function handleClick(){
setCount(count + 1);
setCount(count + 1); // ❌ 结果不是 +2,而是 +1
}
最佳实践:用函数式更新,让React保证基于最新状态计算
jsx
setCount(prev=>prev+1)
2.初始值计算浪费性能
- 坑:如果初始值需要复杂的计算,每次组件渲染都会执行:
jsx
const [value,setValue] = useState(expensiveComputation()); // ❌ 每次 render 都计算
最佳实践:用懒初始化(传函数)
jsx
const [value,setValue] = useState(()=>expensiveComputation()) // z只会执行一次
3.直接修改状态对象或数组,React可能不会触发重新渲染
- 坑:useState 不会做深拷贝,如果直接修改对象的属性/数组,可能不会触发渲染
js
const [user, setUser] = useState({ name: 'Tom' });
user.name = 'Jerry'; // ❌ 不会触发更新
最佳实践:setUser(prev => ({ ...prev, name: 'Jerry' }));
4.状态更新后立即读取
- 坑:setState 不会立即更新state的值,它要等下一次渲染才生效
jsx
setCount(1)
console.log(count) // 还是旧值
最佳实践:把setState的逻辑加到useEffect中
jsx
useEffect(() => {
console.log(count); // ✅ 已更新
}, [count]);
5.频繁更新导致多次渲染
- 坑:连续调用多次setState(不是函数式)的话会触发多次渲染
jsx
setCount(1);
setName('Tom'); // ❌ 两次渲染
最佳实践:
- 同一事件中多次更新,react18会自动批处理
- 如果跨组件调用,可用
unstable_batchedUpdates
手动批处理
jsx
import React, { useState } from "react";
export default function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState("hello");
const handleClick = () => {
setTimeout(() => {
setCount(c => c + 1);
setText("world");
// React 在 setTimeout 中不会自动批处理
// 这里会渲染两次
}, 0);
};
console.log("render"); // 每次渲染都会打印,,渲染两次
return (
<div>
<p>{count} - {text}</p>
<button onClick={handleClick}>更新</button>
</div>
);
}
// unstable_batchUpdates 手动批处理
import React,{useState} from 'react';
import {unstable_batchedUpdates} from 'react-dom';
export default function App() {
const [count, setCount] = useState(0);
const [text, setText] = useState("hello");
const handleClick = ()=>{
setTimeout(()=>{
unstable_batchUpdates(()=>{
setCount(c => c + 1);
setText("world");
// 这里会被批处理,只渲染一次
})
},0)
}
console.log("render"); // 每次渲染都会打印,渲染一次
return (
<div>
<p>{count} - {text}</p>
<button onClick={handleClick}>更新</button>
</div>
);
}
6.状态初始值依赖props
- 坑:useState 的初始值只在首次渲染的时候用,后续props变化不会自动更新。
js
const [value,setValue] = useState(props.defaultValue) // props变了没同步
最佳实践:如果想要state跟着props变,使用useEffect
js
useEffect(() => {
setValue(props.defaultValue);
}, [props.defaultValue]);
7.把状态放得太深
- 坑:状态放在子组件时,父组件没法控制。状态放在父组件时,可能导致不必要的渲染
最佳实践:
- 状态**提升(Lifting State Up)**到需要的最小公共父组件
- 或者用
useReducer
/ 全局状态管理(Redux、Zustand 等)
8.滥用复杂对象当 state
- 坑: 复杂嵌套对象更新麻烦且容易出错:
css
setUser({ ...user, address: { ...user.address, city: 'NY' } });
最佳实践:
- 拆成多个
useState
- 或者用
useReducer
管理复杂结构 坑:
9.**在循环/条件中调用 useState**
- 坑:Hook 调用顺序必须固定,否则 React 会报错:
scss
if (show) {
const [count, setCount] = useState(0); // ❌ 条件调用
}
最佳实践:
- 永远在组件顶层调用 Hook
scss
const [count, setCount] = useState(0);
if (show) { /* 在这里用 count */ }