React项目如何从映射URL中Fetch单条Pokemon卡片实现点击切换捕获状态
修复方案
核心改动说明
你当前的核心问题有两个:
- 全局仅用单一布尔值存储捕获状态,导致所有卡片状态同步,无法单独切换单张卡片的捕获状态
- App 中一次性拉取了所有宝可梦详情,但仅固定取索引 0 的数据,无法动态匹配对应卡片
改造后代码
App.jsx
import Header from './components/Header'; import Footer from './components/Footer'; import Pokedex from './pages/Pokedex'; import Pokemon from './pages/Pokemon'; import Dropdown from './components/Dropdown'; import { useState, useEffect } from 'react'; import { Routes, Route, BrowserRouter as Router } from 'react-router-dom'; import { PokemonContext } from './context/PokemonContext' function App() { const [pokemons, setPokemons] = useState([]); // 用Set存储已捕获的宝可梦ID,替代原单一布尔值,可区分单张卡片状态 const [capturedIds, setCapturedIds] = useState(new Set()) const URL = 'https://pokeapi.co/api/v2/pokemon/?limit=151'; // 存储所有宝可梦详情数据,可按需动态取指定索引的数据 const [pokemonCards, setPokemonCards] = useState([]) const fetchingPokemons = async () => { const res = await fetch(URL); const data = await res.json(); setPokemons(data.results) } useEffect(() => { fetchingPokemons() }, [URL]) const fetchingPokemonCards = async () => { const res = await Promise.all(pokemons.map(pokemon => fetch(pokemon.url).then(res=> res.json()))); setPokemonCards(res) } useEffect(() => { if(pokemons.length > 0) { fetchingPokemonCards() } }, [pokemons]) // 接收宝可梦ID作为参数,仅切换对应卡片的捕获状态 const toggleCaptured = (pokemonId) => { setCapturedIds(prev => { const newSet = new Set(prev) newSet.has(pokemonId) ? newSet.delete(pokemonId) : newSet.add(pokemonId) return newSet }) } const providerValue = { pokemons, pokemonCards, capturedIds, toggleCaptured, } return ( <PokemonContext.Provider value={providerValue}> <Router> <Header /> <Dropdown /> <main className='main'> <Routes> <Route exact path="/" element={<Pokedex />} /> <Route path="/pokemon/:id" element={<Pokemon />} /> </Routes> </main> <Footer /> </Router> </PokemonContext.Provider> ) } export default App;
PokemonCard.jsx
import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import PokemonIcon from './PokemonIcon'; import { useContext } from 'react'; import { PokemonContext } from '../context/PokemonContext'; const PokemonCard = (props) => { const { url } = props const { capturedIds, toggleCaptured } = useContext(PokemonContext) const [pokemonCard, setPokemonCard] = useState({}) const fetchingPokemonCard = async () => { const res = await fetch(url); const data = await res.json(); setPokemonCard(data) } useEffect(() => { fetchingPokemonCard() }, [url]) // 判断当前卡片是否被捕获 const isCaptured = pokemonCard.id ? capturedIds.has(pokemonCard.id) : false return ( <> <div className='pokemon-card' style={{ height: '250px', maxWidth: '250px', margin: '1rem', boxShadow: '5px 5px 5px 4px rgba(0, 0, 0, 0.3)', cursor: 'pointer', }} > <Link to={{ pathname: `/pokemon/${pokemonCard.id}` }} state={{ pokemon: pokemonCard, captured: isCaptured }} style={{ textDecoration: 'none', color: '#000000' }}> <div style={{ padding: '20px', display: 'flex', justifyContent: 'center', alignItems: 'center' }} > <img style={{ height: '100px', width: '100px', }} src={pokemonCard.sprites?.['front_default']} alt='pokemon' /> </div> </Link> <div style={{ textAlign: 'center' }}> <h1 >{pokemonCard.name}</h1> <label > <input type='checkbox' checked={isCaptured} onChange={() => toggleCaptured(pokemonCard.id)} /> <span style={{ marginLeft: 8 }}> {isCaptured ? '已捕获!' : '未捕获!'} </span> </label> </div> </div> </> ) } export default PokemonCard
动态索引取值说明
如果需要动态获取指定索引的宝可梦详情,直接通过 pokemonCards[自定义索引] 取值即可,索引可通过路由参数、点击事件传参等方式自定义。如果不需要全局存储所有宝可梦详情,可直接删除 App 中 fetchingPokemonCards 相关逻辑,复用 PokemonCard 组件内单独拉取当前卡片详情的逻辑即可,无需额外处理索引问题。
内容的提问来源于stack exchange,提问作者geek101
相关产品推荐
相关产品推荐

