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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 21:54:08