React类组件使用Axios请求OpenWeather API出现响应滞后数据跳变问题
问题根源
componentDidUpdate未添加更新前置判断,组件任意更新都会触发接口请求,请求成功后调用setState会再次触发componentDidUpdate,形成无限请求循环,多个异步请求返回顺序不可控,直接导致数据来回跳转- 存在接口请求竞态问题:多次触发请求时,后发起的请求可能比先发起的请求更早返回,旧请求的响应会覆盖新请求的正确数据
setState中直接读取this.state计算派生状态,由于setState是异步批量更新的,可能读取到过期的状态值- 未处理组件卸载时的 pending 请求,若组件卸载后请求才返回,调用
setState会抛出内存泄漏警告
修复方案
- 给
componentDidUpdate添加触发请求的前置判断,仅当城市ID发生变更时才发起新请求,避免无限循环 - 抽离公共请求方法,复用逻辑的同时添加 axios 请求取消逻辑,每次发起新请求前取消前序未完成的请求,彻底解决竞态问题
- 改用函数式
setState计算派生状态,保证读取到的是最新的状态值 - 组件卸载时主动取消未完成的请求,避免内存泄漏
修复后的完整代码示例:
class Weather extends Component { constructor(props) { super(props); this.state = { weatherData: undefined, weatherDescription: undefined, weatherTextDisplay: [], airConditionsText: [] // 注意你原有逻辑中用到的该状态需提前初始化 }; // 存储axios取消请求的方法 this.cancelRequest = null; } // 抽离公共天气请求方法 fetchWeatherData = () => { // 先取消之前未完成的请求,避免旧响应覆盖新数据 if (this.cancelRequest) { this.cancelRequest("取消前序 pending 请求"); } axios .get( `http://api.openweathermap.org/data/2.5/weather?id=${this.props.cityId}&units=metric&appid=myApiKey`, { // 配置取消令牌 cancelToken: new axios.CancelToken(c => { this.cancelRequest = c; }) } ) .then((response) => { if (response.status === 200) { // 函数式setState,获取最新的状态值计算派生数据 this.setState(prevState => ({ weatherData: response.data.main.temp, weatherDescription: response.data.weather[0].description, weatherTextDisplay: prevState.airConditionsText.filter(item => item.id === response.data.weather[0].id) })); } else { throw new Error("请求异常"); } }) .catch((error) => { // 忽略主动取消的请求报错 if (!axios.isCancel(error)) { console.error(error.message); } }); }; componentDidMount() { this.fetchWeatherData(); } componentDidUpdate(prevProps) { // 仅当城市ID变更时才重新请求数据 if (prevProps.cityId !== this.props.cityId) { this.fetchWeatherData(); } } componentWillUnmount() { // 组件卸载时取消未完成的请求,避免内存泄漏 if (this.cancelRequest) { this.cancelRequest("组件卸载,取消请求"); } } // 原有渲染逻辑保持不变 }
内容的提问来源于stack exchange,提问作者hossein fti
相关产品推荐
相关产品推荐

