React应用DOM变更后gettingPokemon函数重复触发问题求助
问题描述
我正在练习React开发,复刻一个宝可梦猜谜应用。应用功能基本正常,但点击正确的宝可梦名称后,图片样式正常变更的同时,辅助函数gettingPokemon会再次触发,导致图片切换——DOM变更时该函数触发了两次。
相关代码如下:
App组件代码
import { useState } from "react"; import { GetImage } from "./components/GetImage"; import { useFetch } from "./hooks/usefetch"; import { gettingPokemon} from "./helpers/gettingRandomPokemon" export const App = () => { const {pokemon1,pokemon2,pokemon3,pokemon4,isLoading} = useFetch(`https://pokeapi.co/api/v2/pokemon`) const [comparacion, setComparacion] = useState(false) const {randomPokemon,urlRandomPokemn} = gettingPokemon(pokemon1,pokemon2,pokemon3,pokemon4) const getName = (e) => { let pickPokemonName = e.target.id if(pickPokemonName === randomPokemon?.name) { setComparacion(true) } else { setComparacion(false) } } return ( <> <h1>quien es ese pokemon?</h1> <hr /> { (!isLoading) && <GetImage urlRandomPokemon={urlRandomPokemn} comparacion={comparacion}></GetImage> } { (isLoading) && <div className="alert alert-info text-center"> Loading... </div> } { (!isLoading) && <> <ul> <li><button onClick={getName} id={pokemon1.name} >{pokemon1.name}</button></li> <li><button onClick={getName} id={pokemon2.name} >{pokemon2.name}</button></li> <li><button onClick={getName} id={pokemon3.name} >{pokemon3.name}</button></li> <li><button onClick={getName} id={pokemon4.name} >{pokemon4.name}</button></li> </ul> </> } </> ) }
gettingPokemon辅助函数代码
export const gettingPokemon = (pokemon1,pokemon2,pokemon3,pokemon4) => { let pokemones = [pokemon1,pokemon2,pokemon3,pokemon4] let randomPokemon = pokemones[Math.floor(Math.random() * pokemones.length)] let urlRandomPokemn = randomPokemon?.sprites.other.dream_world.front_default console.log('me dispare otra vez'); return { randomPokemon, urlRandomPokemn, } }
GetImage组件代码
export const GetImage = ({urlRandomPokemon,comparacion}) => { return ( <img src={urlRandomPokemon} alt='' className={(comparacion) ? 'claro' : 'oscuro'}/> ) }
原因分析
gettingPokemon直接写在App组件的渲染逻辑中,每次组件重新渲染(比如调用setComparacion更新comparacion状态时),这个函数都会重新执行,生成新的随机宝可梦和图片URL,导致图片切换。
另外,React开发环境默认开启的严格模式会让组件渲染执行两次,这也会让gettingPokemon额外触发一次,放大了问题。
解决方案
有两种常用的解决方式:
方式一:用useMemo缓存结果
使用useMemo缓存gettingPokemon的返回值,仅当依赖的宝可梦数据变化时才重新执行函数:
修改App组件代码:
import { useState, useMemo } from "react"; // 引入useMemo // ...其他导入 export const App = () => { const {pokemon1,pokemon2,pokemon3,pokemon4,isLoading} = useFetch(`https://pokeapi.co/api/v2/pokemon`) const [comparacion, setComparacion] = useState(false) // 用useMemo缓存结果,依赖项为四个宝可梦数据 const {randomPokemon,urlRandomPokemn} = useMemo(() => { return gettingPokemon(pokemon1,pokemon2,pokemon3,pokemon4) }, [pokemon1, pokemon2, pokemon3, pokemon4]) // ...getName函数和return部分保持不变 }
这样只有当pokemon1-pokemon4的数据更新时,才会重新生成随机宝可梦,点击按钮更新comparacion状态时不会触发gettingPokemon执行。
方式二:将随机宝可梦存入状态
如果需要在用户答对后刷新随机宝可梦,可以把randomPokemon和urlRandomPokemn存入状态,初始加载时生成,答对后手动刷新:
修改App组件:
import { useState, useEffect } from "react"; // ...其他导入 export const App = () => { const {pokemon1,pokemon2,pokemon3,pokemon4,isLoading} = useFetch(`https://pokeapi.co/api/v2/pokemon`) const [comparacion, setComparacion] = useState(false) const [randomPokemon, setRandomPokemon] = useState(null) const [urlRandomPokemn, setUrlRandomPokemn] = useState('') // 初始加载和宝可梦数据更新时生成随机宝可梦 useEffect(() => { if (!isLoading) { const {randomPokemon: newRandom, urlRandomPokemn: newUrl} = gettingPokemon(pokemon1,pokemon2,pokemon3,pokemon4) setRandomPokemon(newRandom) setUrlRandomPokemn(newUrl) } }, [pokemon1, pokemon2, pokemon3, pokemon4, isLoading]) const getName = (e) => { let pickPokemonName = e.target.id if(pickPokemonName === randomPokemon?.name) { setComparacion(true) // 答对后自动刷新随机宝可梦 const {randomPokemon: newRandom, urlRandomPokemn: newUrl} = gettingPokemon(pokemon1,pokemon2,pokemon3,pokemon4) setRandomPokemon(newRandom) setUrlRandomPokemn(newUrl) } else { setComparacion(false) } } // ...return部分保持不变 }
这种方式更灵活,可自由控制随机宝可梦的刷新时机,比如用户答对后自动更换新题目。
内容的提问来源于stack exchange,提问作者Freddy Alex Saavedra
相关产品推荐
相关产品推荐

