React使用Axios渲染ApexCharts出现卸载组件更新状态报错如何解决
问题原因及修复方案
你遇到的警告是因为组件卸载后,仍有未完成的异步请求执行完毕后尝试更新组件状态导致的,你的代码存在3个核心问题:
- 挂载状态变量命名不一致:constructor和componentDidMount中你使用的是
this._isMount,但componentWillUnmount里写的是this._isMounted,变量名不匹配导致挂载标记完全失效 - 没有在组件卸载时触发Axios请求取消:你已经创建了CancelToken实例,但没有在componentWillUnmount中调用
source.cancel()方法终止未完成的请求 - 嵌套的子请求没有做挂载状态校验:你在循环中发起的获取项目阶段的异步请求回调中,没有判断组件是否已挂载就操作数据、触发状态更新,即使主请求做了校验,子请求返回时组件也可能已经卸载
修正后的完整代码
import React, {Component} from "react"; import GridWrapper from "../helpers/gridWrapper"; import Chart from 'react-apexcharts' import ContentWrapper from "../helpers/contentWrapper"; import GridWrapperL from "../helpers/twoSideBySideWrapper/gridWrapperL"; import axios from "axios"; export default class Charts extends Component{ constructor(props) { super(props); // 统一挂载状态命名 this._isMounted = false; // 把source挂载到实例上,避免全局变量污染 this.source = axios.CancelToken.source(); this.state = { options: { chart: { id: 'apexchart-example' }, xaxis: { categories: [1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999] } }, series: [ { name: '', data: [], }, { name:"In progress stages", data:[], }, { name:"Completed stages", data:[], } ] } } async componentDidMount(){ this._isMounted = true; const todoARR=[] ; const inProgressArr=[]; const readyArr=[]; const project_name=[]; try { //获取项目详情 const projectRes = await axios.get('http://localhost:5000/api/projects', { cancelToken: this.source.token }); const data = projectRes.data.projects; // 用Promise.all等待所有子请求完成,避免异步顺序问题 await Promise.all(data.map(async (element) => { project_name.push(element.projectName); const stageRes = await axios.get(`http://localhost:5000/api/auth/stages/findproject/projectId?id=${element._id}` , { cancelToken: this.source.token }); const data2 = stageRes.data.stage; var todo=0; var inprogress=0; var done=0; //收集每个项目的阶段信息 data2.forEach(element2 => { if(element2.status == "TO-DO"){ todo = todo+1; } if(element2.status == "IN-PROGRESS"){ inprogress = inprogress+1; } if(element2.status == "READY"){ done=done+1; } }) todoARR.push(todo); inProgressArr.push(inprogress); readyArr.push(done); })) //设置Apex图表数据,先判断组件是否挂载 if(this._isMounted){ this.setState({ options: { chart: { id: 'apexchart-example' }, xaxis:{ categories:project_name, } }, series: [ { name: 'Todo stages', data: todoARR, }, { name:"In progress stages", data:inProgressArr, }, { name:"Completed stages", data:readyArr, } ] }) } } catch(err) { // 如果是请求取消的报错,不打印日志 if (!axios.isCancel(err)) { console.log("error", err) } } } componentWillUnmount() { this._isMounted = false; // 取消所有未完成的请求 this.source.cancel('组件卸载,终止未完成请求'); } render(){ return( <center> <div> {/* 只需修改type属性值为line即可切换为折线图 */} <Chart options={this.state.options} series={this.state.series} type="bar" width={1000} height={520} /> </div> </center> ); } };
额外优化说明
把原来forEach嵌套异步请求的逻辑改成了Promise.all等待所有子请求全部返回后再统一更新状态,既避免了多次触发状态更新的性能问题,也解决了异步请求返回顺序不一致导致的数据错乱问题。
内容的提问来源于stack exchange,提问作者Nikita Fernando
相关产品推荐
相关产品推荐

