页面卡顿不一定是网络问题
最近有朋友跟我吐槽,说他做的后台管理页面,点个按钮要等两三秒才有反应。一开始以为是接口慢,结果查了 network 面板发现数据早就回来了。问题出在哪儿?最后定位到是 setState 调得太勤快。
React 里 setState 看着简单,但用得不当,页面立马变“老年机”。
为什么 setState 多了就卡
每次调 setState,React 就会标记组件需要更新,然后进入重新渲染流程。如果一秒内触发几十次,浏览器就得忙死:虚拟 DOM 对比、DOM 更新、重排重绘全来一遍。用户的操作反而被晾在一边,自然就卡了。
比如有个计数器功能,想实现每按一次加一,结果写成了这样:
for (let i = 0; i < 100; i++) {
this.setState({ count: this.state.count + 1 });
}这一下就触发了 100 次更新,页面不卡才怪。
异步事件里也容易踩坑
再比如监听鼠标移动,有人直接拿 setState 记坐标:
handleMouseMove = (e) => {
this.setState({ x: e.clientX, y: e.clientY });
};鼠标一划,几十次事件打过来,setState 跟不要钱似的。其实用户根本看不出这么细的变动,多数都是白算。
怎么解决才靠谱
批量更新是个办法。React 在事件回调里本来就会自动批处理 setState,但如果是 setTimeout 或原生事件,就得自己控制。比如鼠标移动的例子,可以节流一下:
import _ from 'lodash';
constructor() {
super();
this.handleMouseMove = _.throttle(this.updatePosition, 50);
}
updatePosition = (e) => {
this.setState({ x: e.clientX, y: e.clientY });
};这样一秒钟最多更新 20 次,体验够用,性能也扛得住。
函数式 setState 减少无效更新
如果新状态依赖旧状态,别用 this.state 直接算,用函数形式:
this.setState((prevState) => ({
count: prevState.count + 1
}));这样即使多次调用,也能保证结果正确,避免因状态延迟导致的重复计算和多余渲染。
该用 useRef 的时候别硬上 setState
有些数据变了不需要界面刷新,比如存个定时器 id 或临时标记。这时候往 state 里塞,纯粹是给 React 找活干。换成 useRef 存,改值不触发更新,干净利落。
卡顿问题很多时候不是框架不行,而是用法太直白。把 setState 当普通变量赋值用,迟早出事。多想想“这次更新真有必要吗”,页面顺滑不少。