React+Typescript:修复 UseEffect Hook(回调+清理函数错误)
Posted
技术标签:
【中文标题】React+Typescript:修复 UseEffect Hook(回调+清理函数错误)【英文标题】:React+Typescript: Fixing UseEffect Hook ( Callback + clean up function errors) 【发布时间】:2022-01-11 13:14:45 【问题描述】:我正在完成一项让我发疯的学校作业。这是一个小游戏,用户点击两张仓鼠图片中的一张来挑选最可爱的获胜者。
为此我不得不使用 React + Typescript。
游戏现在运行良好,但我在控制台中收到两条消息,我必须在发送作业之前修复这些消息,但我不明白怎么做,尤其是因为我觉得打字稿让它变得更加困难.
一个是:'sendRequest' 函数使 useEffect Hook(第 23 行)的依赖关系在每次渲染时都发生变化。将它移到 useEffect 回调中。或者,将 'sendRequest' 的定义包装在它自己的 useCallback() Hook react-hooks/exhaustive-deps 中
另一个是:警告:无法对未安装的组件执行 React 状态更新。这是一个空操作,但它表明您的应用程序中存在内存泄漏。要解决此问题,请在 useEffect 清理函数中取消所有订阅和异步任务。
第二个只有在我开始新游戏后才会出现。
这两个错误都发生在我的“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);
async function sendRequest(saveUpdatedData: any)
const response = await fetch('/hamsters/'+ fighter);
const data = await response.json()
saveUpdatedData(data)
useEffect(() =>
sendRequest(setUpdatedData)
, [showInfo, sendRequest])
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
任务要求在用户选择获胜者后,弹出更多关于用户可以阅读的每只仓鼠的信息,因此是overaly。为了获得更新的游戏数量和每只仓鼠获胜/失败的正确信息,我必须在用户选择获胜者后再次从服务器获取数据。为此,我在 UseEffect 上添加了它应该在每次切换覆盖层更改的状态 (showInfo) 时运行,并且它作为道具从渲染覆盖层的“游戏”组件发送。
代码如下:
import useEffect, useState from 'react';
import Hamster from '../../models/Hamster';
import FighterCard from './FighterCard'
import '../../styles/game.css'
import FighterCardOverlay from './FighterCardOverlay';
type Hamsters = Hamster;
const Game = () =>
const [fighters, setFighters] = useState<Hamsters[] | null >(null);
const [showInfo, setShowInfo] = useState(false);
useEffect(() =>
getFighters(setFighters)
, [])
function handleShowInfo()
if(showInfo === true )
setShowInfo(false)
else if(showInfo === false)
setShowInfo(true)
function newGame()
getFighters(setFighters)
handleShowInfo()
function updateWin(id: string, wins: number, games: number)
const newWins = wins + 1;
const newGames = games + 1;
return fetch('/hamsters/' + id,
method: 'PUT',
headers: "Content-Type": "application/json" ,
body: JSON.stringify(wins: newWins, games: newGames)
)
function updateDefeats(id:string, defeats: number, games: number)
const newDefeats = defeats + 1;
const newGames = games + 1;
return fetch('/hamsters/' + id,
method: 'PUT',
headers: "Content-Type": "application/json" ,
body: JSON.stringify(defeats: newDefeats, games: newGames)
)
function updateWinnersAndLosers(winnerId: string)
if( fighters )
const winner = fighters.find(fighter => fighter.id === winnerId);
const loser = fighters.find(fighter => fighter.id !== winnerId);
if(winner)
updateWin(winner.id, winner.wins, winner.games);
if(loser)
updateDefeats(loser.id, loser.defeats, loser.games);
;
handleShowInfo();
else
console.log('fighters is undefined')
return (
<div className='game'>
<h2>Let the fight begin!</h2>
<p>Click on the cutest hamster</p>
<section className="fighters">
fighters
? fighters.map(fighter => (
<div className="fighterCardContainer" key=fighter.id>
<FighterCard fighter=fighter updateWinnersAndLosers=updateWinnersAndLosers />
showInfo && <FighterCardOverlay fighter=fighter.id showInfo=showInfo/>
</div>
))
: 'Loading fighters...'
</section>
<button className="gameButton" onClick=newGame>New Game</button>
</div>
)
;
async function getFighters(saveFighters: any)
try
const response = await fetch('/hamsters');
const data = await response.json()
const fighters = await data.sort(() => .5 - Math.random()).slice(0,2)
saveFighters(fighters)
catch (error)
saveFighters(null)
;
export default Game
正如我所说,游戏运行良好,我得到了正确的信息并且它没有崩溃。但是,对于修复控制台中的错误的任何帮助将不胜感激!
【问题讨论】:
【参考方案1】:可以这样解决:
useEffect(() =>
const controller = new AbortController(); // It solves Warning: Can't perform a React state update on an unmounted component.
const signal = controller.signal;
(async () => // you also can move it to useCallback (don't forget pass signal
try
const response = await fetch('/hamsters/' + fighter, signal );
const data = await response.json();
setUpdatedData(data);
catch (error)
// TODO: handle error
)();
return () =>
controller.abort();
;
, [showInfo, fighter]);
【讨论】:
非常感谢!我刚试了一下,控制台中的所有消息都消失了!但似乎出现了一个新问题。加载页面时,我没有获得第一个游戏的正确更新数据?不过,它确实适用于我之后开始的新游戏。我不明白这可能是什么原因。有什么想法吗? 我不确定我的代码是否会导致您出现新问题。以前,您的效果会运行每个渲染。但现在只有当 shoInfo 和 fighter 被改变时。更改依赖项全部删除它们,useEffect(() => /*effect*/)
删除依赖项成功了!谢谢!
如果我帮助你,你能标记我的答案吗?以上是关于React+Typescript:修复 UseEffect Hook(回调+清理函数错误)的主要内容,如果未能解决你的问题,请参考以下文章
typescript eventTarget dataset 如何修复报错?