React+Typescript:修复UseEffect Hook(回调+清理函数错误)
React+Typescript: Fixing UseEffect Hook ( Callback + clean up function errors)
我正在做一项让我抓狂的学校作业。这是一个小游戏,用户可以点击两张仓鼠图片中的一张来选出最可爱的那只。
为此我不得不使用 React + Typescript。
游戏现在运行得很好,但我在控制台收到两条消息,我必须在发送作业之前修复这些消息,但我不明白如何解决,尤其是因为我觉得 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
作业要求在用户选择获胜者后,弹出关于用户可以阅读的每只仓鼠的更多信息,因此总体而言。为了获得更新的游戏数量和每只仓鼠 wins/defeats 的正确信息,我必须在用户选择获胜者后再次从服务器获取数据。为此,我在 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
正如我所说,游戏运行良好,我获得了正确的信息并且没有崩溃。但是,如果您能帮助修复控制台中的错误,我们将不胜感激!
可以这样解决:
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]);
我正在做一项让我抓狂的学校作业。这是一个小游戏,用户可以点击两张仓鼠图片中的一张来选出最可爱的那只。
为此我不得不使用 React + Typescript。
游戏现在运行得很好,但我在控制台收到两条消息,我必须在发送作业之前修复这些消息,但我不明白如何解决,尤其是因为我觉得 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
作业要求在用户选择获胜者后,弹出关于用户可以阅读的每只仓鼠的更多信息,因此总体而言。为了获得更新的游戏数量和每只仓鼠 wins/defeats 的正确信息,我必须在用户选择获胜者后再次从服务器获取数据。为此,我在 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
正如我所说,游戏运行良好,我获得了正确的信息并且没有崩溃。但是,如果您能帮助修复控制台中的错误,我们将不胜感激!
可以这样解决:
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]);