Next.js useState useEffect useRef 速记
一句话速记:
- useState:保存“会驱动界面”的数据,改了就触发重新渲染。
- useEffect:做“副作用”(请求、订阅、定时器、操作 DOM)。可清理。
- useRef:保存“不会触发渲染”的可变值或 DOM 节点,整个生命周期都能拿到。
useState(渲染用数据)
- 改变 state 会触发重新渲染。
- 依赖上一次值时,用函数式更新。
const [count, setCount] = useState(0);// 基础更新
setCount(3);// 基于上一次值更新(更安全)
setCount(prev => prev + 1);
小贴士:不要直接修改对象/数组,返回新值;复杂初始值可用 useState(() => heavyInit()) 延迟计算。
useEffect(副作用与清理)
- 什么时候执行:
- 无依赖:每次渲染后执行。
- 空依赖 []:只在首次渲染后执行一次。
- 有依赖 [a, b]:首次+当依赖变化时执行。
- 返回清理函数:依赖变化或组件卸载前会先执行。
// 首次执行一次 + 卸载时清理
useEffect(() => {const id = setInterval(() => console.log('tick'), 1000);return () => clearInterval(id); // 清理
}, []);// 依赖变化时先清理上一次再重建
useEffect(() => {document.title = `count = ${count}`;return () => { /* 可选清理 */ };
}, [count]);
常见用途:请求/取消、事件监听/移除、定时器/清理、读写 DOM。
useRef(跨渲染持久可变容器)
- 改 ref.current 不会触发渲染;引用对象本身稳定不变。
- 两大用途:存任意可变值(定时器 id、缓存、上次值)、拿 DOM 节点。
// 1) 存可变值:定时器 id
const timerIdRef = useRef<number | null>(null);
function start() {if (timerIdRef.current) clearTimeout(timerIdRef.current);timerIdRef.current = window.setTimeout(() => console.log('done'), 1000);
}// 2) 拿 DOM:React 挂载时自动赋值 ref.current = 真实 DOM
const divRef = useRef<HTMLDivElement | null>(null);
return <div ref={divRef}>Hello</div>;// 3) 解决闭包拿旧值:总拿到“最新值”
const latestCount = useRef(count);
useEffect(() => { latestCount.current = count; });
该用谁
- 需要显示在界面上、改了就应更新界面 → useState
- 需要在渲染之外与外界交互(请求、订阅、DOM、定时器) → useEffect
- 需要记住某个值或 DOM,但不想触发渲染/避免闭包问题 → useRef
易错点速查
- useEffect 依赖别漏,装 eslint-plugin-react-hooks 的 exhaustive-deps。
- 定时器/订阅要清理;依赖变更会先执行上一次清理。
- 基于旧 state 计算新值时用函数式更新。
- 访问 DOM 用 ref.current,不是 ref 本身。