You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 02:05:18