ReactJS页面刷新时PokemonCard组件API数据为何变为初始空对象?
React State与useEffect问题:刷新后状态回退至初始值
问题描述
在PokemonCard组件的pokemonTypes方法中,首次加载页面时控制台能打印更新后的pokemonData状态;但刷新页面后,控制台打印的却是初始状态的空对象。
相关代码
App.js
import './App.css'; import { useState, useEffect } from 'react'; import PokemonCard from './components/PokemonCard'; const interval = { limit: 25 } const App = (props) => { const { pokedex, } = props const [pokemonList, setPokemonList] = useState([]) useEffect(() => { const getPokemonListData = async () => { let pokemon = await pokedex.getPokemonsList(interval) setPokemonList(pokemon.results) } getPokemonListData() }, []) return ( <div className="App"> <h1>Pokedecs</h1> <div className="container"> { pokemonList?.map(poke => { return <PokemonCard key={poke.name} pokedex={pokedex} pokemonName={poke.name} /> }) } </div> </div> ); } export default App;
PokemonCard.js
import { useEffect, useState } from "react" import PokemonTypePill from "./PokemonTypePill" import '../App.css'; const PokemonCard = (props) => { let styles = { card: { backgroundColor: "#fff", height: "200px", width: "150px", boxShadow: '1px 2px 5px #000000', margin: "15px", textAlign: "center", borderRadius: "12px", fontWeight: "bold", transition: "transform .2s" } } const {pokemonName, pokedex, } = props const [pokemonData, setPokemonData] = useState({}) const getPokemonData = async () => { let pokemon = await pokedex.getPokemonByName(pokemonName) setPokemonData(pokemon) } const pokemonTypes = () => { console.log(pokemonData) //this always uses the initial state "{}" } useEffect(() => { getPokemonData() pokemonTypes() }, [setPokemonData]) return ( <div className="card" style={styles.card}> <img src={pokemonData.sprites && pokemonData.sprites?.front_default} /> <p>#{pokemonData.id} {pokemonName.charAt(0).toUpperCase() + pokemonName.slice(1)}</p> <div style={{width: "inherit", display: "flex", alignContent: "center", justifyContent: "center"}}> { pokemonData.types?.map((pokemonType) => { return ( <PokemonTypePill key={pokemonType.type.name} typeName={pokemonType.type.name} /> ) }) } </div> </div> ) } export default PokemonCard
问题原因
- useEffect依赖项错误:当前
useEffect的依赖是setPokemonData,但setPokemonData是React内部稳定的函数,不会触发副作用重新执行。同时,getPokemonData是异步函数,调用后立刻执行pokemonTypes,此时setPokemonData还未完成状态更新,pokemonData仍为初始空对象。首次加载的偶然正常是React渲染时序的巧合,不具备稳定性。 - 未监听状态变化:
pokemonTypes依赖pokemonData的更新,但没有在状态变化时触发执行。
解决方案
步骤1:修正useEffect依赖项
将请求数据的useEffect依赖项改为pokemonName和pokedex,确保当props变化时重新请求数据,移除不必要的setPokemonData依赖。
步骤2:监听pokemonData状态变化
新增一个useEffect,专门监听pokemonData的变化,当状态更新后再执行pokemonTypes。
修改后的PokemonCard核心代码
// ...其他代码不变 const getPokemonData = async () => { let pokemon = await pokedex.getPokemonByName(pokemonName) setPokemonData(pokemon) } const pokemonTypes = () => { console.log(pokemonData) } // 负责请求数据的副作用 useEffect(() => { getPokemonData() }, [pokemonName, pokedex]) // 依赖props,确保props变化时重新请求 // 监听pokemonData变化,状态更新后执行pokemonTypes useEffect(() => { if (Object.keys(pokemonData).length > 0) { // 避免初始空对象时执行 pokemonTypes() } }, [pokemonData]) // ...其他代码不变
补充说明
- React的状态更新是异步的,不能在调用
setState后立刻获取更新后的状态值。 - 副作用的依赖项必须包含所有在副作用中使用的外部变量,否则会导致闭包问题,拿到旧的状态或props值。
内容的提问来源于stack exchange,提问作者DCWD
相关产品推荐
相关产品推荐

