ReactJS页面刷新后Pokemon列表数据消失问题求助
问题分析与解决方案
你的核心问题在于第二个useEffect的依赖数组为空,导致它只会在组件首次挂载时执行一次。此时第一个useEffect里的异步fetch还没完成,pokemonList还是初始的空数组,所以populateRenderedList生成的是空列表。当后续pokemonList被异步请求填充后,第二个useEffect不会重新执行,renderedList也就一直保持为空。
另外,renderedList属于派生状态——它完全由pokemonList计算而来,根本不需要用useState存储。之前不用useState时能正常工作,就是因为每次渲染都会基于最新的pokemonList重新计算列表内容。
方案一:移除renderedList的状态存储(推荐)
直接在渲染阶段调用populateRenderedList,这样每次pokemonList更新时都会自动生成最新的渲染列表:
function PokemonList(props) { const [pokemonList, setPokemonList] = useState([]); const [isFetched, setIsFetched] = useState(false); useEffect(() => { fetch(raw) .then((r) => r.text()) .then((text) => { setPokemonList(text.split("\n")); setIsFetched(true); }); }, []); // 直接在渲染时计算派生状态 const renderedList = populateRenderedList(pokemonList); return !isFetched ? null : ( <div className="list"> {renderedList} <PaginationBar listSize={renderedList.length} list={renderedList} /> </div> ); } function populateRenderedList(pokemonList) { const pokemonAPI = "https://pokeapi.co/api/v2/pokemon-form/"; // 过滤空行(避免split后出现空字符串项) return pokemonList.filter(Boolean).map((pokemonName) => ( <a key={pokemonName} // 必须添加唯一key,React列表渲染要求 className="pokemonLink" href={`${pokemonAPI}${pokemonName.toLowerCase()}`} > <PokemonDiv name={pokemonName.toLowerCase()} /> <h3>{pokemonName}</h3> </a> )); }
方案二:修正useEffect的依赖数组(不推荐,因为没必要存派生状态)
如果你非要用useState存储renderedList,需要把pokemonList加入第二个useEffect的依赖数组,确保pokemonList更新时重新计算:
function PokemonList(props) { const [pokemonList, setPokemonList] = useState([]); const [isFetched, setIsFetched] = useState(false); const [renderedList, setRenderedList] = useState([]); useEffect(() => { fetch(raw) .then((r) => r.text()) .then((text) => { setPokemonList(text.split("\n")); setIsFetched(true); }); }, []); // 添加pokemonList到依赖数组 useEffect(() => { setRenderedList(populateRenderedList(pokemonList)); }, [pokemonList]); return !isFetched ? null : ( <div className="list"> {renderedList} <PaginationBar listSize={renderedList.length} list={renderedList} /> </div> ); } // 同样要给列表项添加key,并过滤空行 function populateRenderedList(pokemonList) { const pokemonAPI = "https://pokeapi.co/api/v2/pokemon-form/"; const temp = []; pokemonList.filter(Boolean).forEach((pokemonName) => { let renderedPokemon = ( <a key={pokemonName} className="pokemonLink" href={`${pokemonAPI}${pokemonName.toLowerCase()}`} > <PokemonDiv name={pokemonName.toLowerCase()} /> <h3>{pokemonName}</h3> </a> ); temp.push(renderedPokemon); }); return temp; }
额外注意事项
- 列表渲染必须给每个子项添加唯一
key,否则React会出现性能问题和渲染错误。 text.split("\n")可能会产生空字符串项(比如文件末尾的换行),建议用filter(Boolean)过滤掉。
内容的提问来源于stack exchange,提问作者user21242991
相关产品推荐
相关产品推荐

