React+TypeScript项目useEffect依赖及组件卸载状态更新报错修复
仓鼠投票小游戏控制台报错修复方案
报错1:useEffect依赖变动告警
- 报错原因:
sendRequest函数定义在组件顶层,每次组件渲染都会重新生成新的函数实例,导致useEffect的依赖项每次渲染都变动,触发react-hooks/exhaustive-deps规则校验失败。 - 修复方法:将
sendRequest函数移到useEffect回调内部,无需再将函数作为依赖项传入,依赖数组仅保留实际用到的外部变量showInfo和fighter即可。
报错2:组件卸载后更新状态告警
- 报错原因:点击新游戏按钮时,
showInfo状态变为false,FighterCardOverlay组件直接被卸载,但组件此前发起的接口请求仍在进行,请求完成后调用setUpdatedData更新已卸载组件的状态,触发内存泄漏警告。 - 修复方法:
- 在
useEffect中新增AbortController控制器,请求时绑定signal参数,组件卸载时调用abort中断未完成的请求 - 新增组件挂载状态标记,仅当组件仍处于挂载状态时才执行状态更新操作
- 清理函数中额外重置状态,避免下次组件挂载时闪现旧数据
- 在
完整修复后的FighterCardOverlay组件代码
import { Hamster } from '../../models/Hamster'; import { useEffect, useState } from 'react'; interface CardGridProps { fighter: string; showInfo: boolean; } type Hamsters = Hamster; const FighterCardOverlay = ({ fighter, showInfo }: CardGridProps) => { const [updatedData, setUpdatedData] = useState<Hamsters | null>(null); useEffect(() => { let isMounted = true; const controller = new AbortController(); const sendRequest = async () => { try { const response = await fetch(`/hamsters/${fighter}`, { signal: controller.signal }); const data = await response.json(); if (isMounted) { setUpdatedData(data); } } catch (err) { if ((err as Error).name !== 'AbortError') { console.error('请求仓鼠数据失败:', err); } } }; if (showInfo) { sendRequest(); } return () => { isMounted = false; controller.abort(); setUpdatedData(null); }; }, [showInfo, fighter]); return ( <div className="fighterCardOverlay" > {updatedData ? <div> <li>Name: {updatedData.name}</li> <li>Age: {updatedData.age}</li> <li>Loves: {updatedData.loves}</li> <li>Favorite Food: {updatedData.favFood}</li> <li>Games: {updatedData.games}</li> <li>Wins: {updatedData.wins}</li> <li>Defeats: {updatedData.defeats}</li> </div> : 'Loading info...' } </div> ) } export default FighterCardOverlay
额外说明
不需要修改父组件Game的代码,替换FighterCardOverlay组件后即可消除所有控制台报错,原有游戏逻辑完全不受影响。
内容的提问来源于stack exchange,提问作者Marica
相关产品推荐
相关产品推荐

