React重新请求数据时页面卡顿冻结是什么原因?
问题根因
- 死循环触发无限渲染:
componentDidUpdate生命周期中,判断startDate变更后,又调用updateStartDate、updateEndDate重复设置相同的state,会再次触发componentDidUpdate,形成无限递归循环,CPU和内存占用持续升高,多次触发后就会导致页面冻结。你添加的shouldComponentUpdate刚好拦截了部分无效更新,所以问题有轻微缓解。 - 未处理请求竞态与内存泄漏:多次发起的请求没有取消机制,旧请求返回后依然会执行
setState触发不必要的渲染,短时间内大量请求叠加会占用过多浏览器资源。同时componentDidMount中的setTimeout没有清理逻辑,组件卸载后仍可能执行,也会引发内存泄漏。 - 状态更新逻辑冗余:日期变更的状态更新应该由日期选择组件的回调触发,不需要在
componentDidUpdate中重复设置已经变更完成的状态。
解决方案
1. 修复componentDidUpdate死循环
直接移除componentDidUpdate中冗余的状态更新调用,修改后代码如下:
componentDidUpdate(prevProps, prevState){ if(this.state.startDate !== prevState.startDate || this.state.endDate !== prevState.endDate){ // 只需要重新发起请求,不要重复设置已经更新的startDate/endDate this.fetchData(this.state.startDate, this.state.endDate); this.fetchUniqueData(this.state.startDate, this.state.endDate) } } // 这两个函数保留,给日期选择器的onChange事件调用即可 updateStartDate = (start) => { this.setState({startDate: start}) } updateEndDate = (end) => { this.setState({endDate: end}) }
2. 添加请求取消机制
使用AbortController在发起新请求前取消上一次未完成的请求,避免无效更新:
class YourComponent extends React.Component { // 声明abort控制器和定时器id fetchAbortController = null; timerId = null; async fetchData(start, end) { try { // 取消上一次未完成的请求 if(this.fetchAbortController) this.fetchAbortController.abort(); this.fetchAbortController = new AbortController(); const URL = "exampleUrl"; var myHeaders = new Headers(); myHeaders.append("Authorization", "exampleToken"); let formdata = new FormData(); formdata.append("act", "report1"); formdata.append("data", `{"start_date": "${start}", "end_date": "${end}"}`); var requestOptions = { method: "POST", headers: myHeaders, body: formdata, redirect: "follow", // 关联abort信号 signal: this.fetchAbortController.signal }; const response = await fetch(URL, requestOptions) const json = await response.json() this.setState({data: json.data}) } catch (err) { // 忽略abort产生的错误 if(err.name !== 'AbortError') console.log(err); } } componentDidMount() { this.currentDate(); this.addRow() this.fetchData(this.state.startDate, this.state.endDate); this.fetchUniqueData(this.state.startDate, this.state.endDate); this.handleRow(); // 保存定时器id this.timerId = setTimeout(() => { for (let i = 0; i < 11; i++) { this.fillCheckboxes(i); } }, 500); } componentWillUnmount() { // 组件卸载时取消请求和清除定时器 if(this.fetchAbortController) this.fetchAbortController.abort(); if(this.timerId) clearTimeout(this.timerId); } // 其他代码... }
3. 优化渲染判断逻辑
完善shouldComponentUpdate的判断条件,覆盖所有需要触发渲染的状态字段:
shouldComponentUpdate(nextProps, nextState) { return this.state.startDate !== nextState.startDate || this.state.endDate !== nextState.endDate || this.state.data !== nextState.data; }
如果组件的状态都是基础类型或浅比较可区分的引用类型,也可以直接继承React.PureComponent,自动实现浅比较的shouldComponentUpdate。
4. 可选优化:添加请求防抖
如果日期选择器支持连续调整(比如日期滚轮),可以给请求添加300ms左右的防抖,避免短时间内发起大量无效请求。
内容的提问来源于stack exchange,提问作者Shota Donguzashvili
相关产品推荐
相关产品推荐

