React点击单张PokemonCard仅切换对应卡片捕获状态问题求解
问题核心
你定义的captured是全局共享的布尔状态,所有卡片绑定的是同一个状态值,任意修改都会触发所有卡片同步更新,这是所有卡片状态一起变化的根本原因。
修复方案
将全局布尔状态替换为已捕获宝可梦ID的集合,每次点击仅对当前选中ID的状态做变更,每个卡片单独判断自身ID是否在集合中,得到独立的捕获状态。
完整修改代码
父组件 Pokedex.jsx
import React, { useState, useEffect } from 'react' import PokemonCard from '../components/PokemonCard'; const Pokedex = () => { const [pokemons, setPokemons] = useState([]); // 替换为存储已捕获ID的Set结构 const [capturedIds, setCapturedIds] = useState(new Set()) const URL = 'https://pokeapi.co/api/v2/pokemon/?limit=151'; const fetchingPokemons = async () => { const res = await fetch(URL); const data = await res.json(); // 从url中提取宝可梦ID,添加到每个pokemon对象中 const pokemonWithId = data.results.map(item => { const id = item.url.split('/').filter(Boolean).pop() return {...item, id} }) setPokemons(pokemonWithId) } useEffect(() => { fetchingPokemons() }, [URL]) const toggleCaptured= (checked, id) => { setCapturedIds(prev => { const newCaptured = new Set(prev) if(checked) { newCaptured.add(id) } else { newCaptured.delete(id) } // 这里可以做已捕获/未捕获的统计逻辑 const capturedPkm = pokemons.filter(item => newCaptured.has(item.id)) const notCapturedPkm = pokemons.filter(item => !newCaptured.has(item.id)) console.log('captured', capturedPkm, 'not captured', notCapturedPkm) return newCaptured }) } return ( <> <div style={{display: 'flex', flexWrap: 'wrap', justifyContent: 'space-evenly'}}> {pokemons.length ? pokemons.map((pokemon) => { return ( <div style={{ width: '235px' }} key={pokemon.id} > <PokemonCard pokemon={pokemon} name={pokemon.name} url={pokemon.url} // 单独传递当前卡片的捕获状态 captured={capturedIds.has(pokemon.id)} toggleCaptured={toggleCaptured} /> </div> ) }) : <h1>Loading...</h1>} </div> </> ) } export default Pokedex
子组件 PokemonCard.jsx
import React, { useState, useEffect } from 'react'; import { Link } from 'react-router-dom'; import PokemonIcon from './PokemonIcon'; const PokemonCard = (props) => { const { url, captured, toggleCaptured } = props const [pokemonCard, setPokemonCard] = useState({}) const fetchingPokemonCard = async () => { const res = await fetch(url); const data = await res.json(); setPokemonCard(data) } useEffect(() => { fetchingPokemonCard() }, [url]) 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 }} style={{ textDecoration: 'none', color: '#000000' }}> <div style={{ padding: '20px', display: 'flex', justifyContent: 'center', alignItems: 'center' }} > <PokemonIcon img={pokemonCard.sprites?.['front_default']} /> </div> </Link> <div style={{ textAlign: 'center' }}> <h1 >{pokemonCard.name}</h1> <label > <input type='checkbox' // 替换defaultChecked为受控的checked属性,和父组件状态同步 checked={captured} onChange={(e) => toggleCaptured(e.target.checked, pokemonCard.id)} /> <span style={{ marginLeft: 8, cursor: 'pointer' }}> {captured ? 'Captured!' : 'Not captured!'} </span> </label> </div> </div> </> ) } export default PokemonCard
内容的提问来源于stack exchange,提问作者geek101
相关产品推荐
相关产品推荐

