如何解决React中已卸载组件执行setState报错的相关问题
为什么点击搜索按钮会执行清理代码
useEffect的清理函数不止在组件卸载时执行,只要useEffect的依赖项发生变化,React都会先执行上一次渲染生成的effect返回的清理函数,再执行本次渲染对应的新effect。你这里的useEffect依赖了search状态,点击搜索按钮会调用setSearch修改search的值,自然会触发上一次effect的清理逻辑。
你最开始用普通let mounted = true的写法能跑通,本质是每次组件重渲染时都会重新创建这个mounted变量并赋值为true,相当于每次新的effect用的都是新的变量,旧的清理函数修改的是上一次渲染的mounted变量,互不干扰。但这种写法不符合React的状态持久化规则,所以ESLint会报错。你用useRef的时候出错是写法不对,不是useRef本身的问题。
远端搜索的正确实现方式
推荐优先用AbortController取消过期请求,比单纯判断组件是否挂载更合理,既能避免state更新警告,还能减少不必要的网络请求,示例代码如下:
function ListStock() { const [data, setData] = useState([]); const [search, setSearch] = useState(); useEffect(() => { // 每次effect创建新的AbortController实例 const controller = new AbortController(); const signal = controller.signal; async function fetchData() { try { const {start_date, end_date} = search; // 请求的时候传入signal参数,fetch/axios等主流请求库都支持 const result = await getDataStock(start_date, end_date, signal); if (!signal.aborted) { // 只处理未被取消的请求结果 setData(result.data); } } catch (err) { // 请求被取消的错误属于预期行为,不用处理 if (err.name !== 'AbortError') { console.error('请求失败', err); } } } if (search) { // 避免search为undefined时报解构错误 fetchData(); } // 依赖变化/组件卸载时取消当前请求 return () => { controller.abort(); } }, [search]) const handleSearch = () => { setSearch({ start_date: moment().subtract(1, 'month').format('YYYY-MM-DD'), end_date: moment().format('YYYY-MM-DD') }); } return ( <div> <input type="text" id="keyword"> <input type="button" onClick={handleSearch} value="Search"> {data} </div> ) }
如果你坚持要用mounted标记的方式,正确的useRef写法如下:
function ListStock() { const isMountedRef = useRef(true); const [data, setData] = useState([]); const [search, setSearch] = useState(); // 空依赖的useEffect只在组件挂载/卸载时执行,专门维护挂载状态 useEffect(() => { isMountedRef.current = true; return () => { isMountedRef.current = false; } }, []); useEffect(() => { async function fetchData() { const {start_date, end_date} = search; const result = await getDataStock(start_date, end_date); if (result && isMountedRef.current) { setData(result.data); } } if (search) { fetchData(); } }, [search]) // 其余逻辑保持不变 }
额外可以给搜索按钮加点击防抖,避免用户短时间多次点击发起重复请求。
能不能忽略这类ESLint警告
绝对不建议。这类警告是ESLint基于React的运行规则检测出的潜在逻辑问题,忽略之后表面上不会直接报错,但会留下内存泄漏、旧请求结果覆盖新请求、状态更新异常等隐藏bug,后续排查成本极高。
内容的提问来源于stack exchange,提问作者Bar .W
相关产品推荐
相关产品推荐

