如何修复React应用设置state时出现的内存泄漏问题
问题原因
该警告触发逻辑为:你在ConcertDetails组件挂载后发起了异步网络请求,在请求还未返回结果时就跳转页面离开了详情页,此时ConcertDetails组件已经被React卸载,但异步请求仍在后台运行,当请求成功后调用setDetails、setSongs更新已卸载组件的状态,就会触发该内存泄漏警告。
另外你现有代码的useEffect没有声明依赖数组,会导致组件每次渲染都重复发起请求,属于不必要的性能损耗,且你原本的axios传参格式有误,headers配置需要放在第二个参数中。
修复方案
推荐使用Axios支持的AbortController直接取消未完成的请求,从根源避免后续的状态更新操作,修改后的ConcertDetails.js代码如下:
import React, { useState, useEffect } from 'react'; import axios from '../utils/API'; const ConcertDetails = (Props) => { const [details, setDetails] = useState({}); const [songs, setSongs] = useState([]); useEffect(() => { // 声明AbortController用于取消未完成的请求 const controller = new AbortController(); const url = '/concerts/' + Props.match.params.id.toString() + '/'; const getDetails = async () => { try { const response = await axios.get(url, { signal: controller.signal, // 把控制器和当前请求关联 headers: {'Content-Type': 'application/json'} }); setDetails(response.data); setSongs(response.data.song); } catch (err) { // 如果是请求主动取消的报错,不需要额外处理 if (axios.isCancel(err)) return; // 其他请求错误可以在这里补充你的业务处理逻辑 console.error('请求失败:', err); } }; getDetails(); // 清理函数,组件卸载时自动执行,取消未完成的请求 return () => controller.abort(); // 加依赖数组,只有页面ID变化时才重新发起请求,避免无限重复请求 }, [Props.match.params.id]); const getSongs = () => { return songs.map((song) => ( <li key={song.title}>{song.title}</li> )); } return ( <> <h1>Concert Details</h1> <p>Venue: {details.venue}</p> <p>Date: {details.date}</p> <ul> {getSongs()} </ul> </> ) } export default ConcertDetails;
补充说明
如果不想用取消请求的方式,也可以用挂载标记位的方案:在useEffect内声明一个isMounted变量初始值为true,清理函数里把它设为false,请求成功后先判断isMounted为true再更新状态。不过直接取消请求是更优的方案,还能减少不必要的网络资源占用。
内容的提问来源于stack exchange,提问作者Jake Mulhern
相关产品推荐
相关产品推荐

