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

React使用useState更新数组时仅保留最新点击元素问题求解

问题原因及修复方案

核心问题梳理

  • 状态存储位置错误
    你把capturedPkm状态定义在了PokemonCard组件内部,这意味着每个宝可梦卡片都有独立的、互不共享的capturedPkm数组。点击A卡片更新的是A组件自己的数组,点击B卡片更新的是B组件自己的数组,自然每次只能看到当前点击的宝可梦。这个状态需要提升到所有PokemonCard的公共父组件中存储,再把状态和更新方法通过props传递给每个卡片。
  • 数组更新逻辑错误
    就算不考虑状态位置的问题,你当前的更新逻辑也存在问题:
    你已经基于旧数组拷贝得到了currentChecked,并且把当前宝可梦名push进去了,更新的时候直接把这个数组传给setCapturedPkm即可,原写法[...capturedPkm, currentChecked]相当于把整个新数组作为元素塞进旧数组,会得到嵌套结构,完全不符合需求;同时你没有处理取消选中的场景,取消勾选时没有把对应宝可梦从数组中移除;另外React的状态更新是异步的,调用setCapturedPkm后立刻打印capturedPkm拿到的还是更新前的旧值,不能用来验证更新结果。
  • 冗余状态问题
    你定义了notCapturedPkm但全程没用到,可以直接删掉;也不需要单独存储label状态,直接根据当前宝可梦是否在捕获数组中动态计算即可。

修复后代码

公共父组件示例

// 父组件示例,比如PokemonList.js
import React, { useState } from 'react'
import PokemonCard from './PokemonCard'

const PokemonList = () => {
  // 把捕获状态移到父组件,所有卡片共享
  const [capturedPkm, setCapturedPkm] = useState([])

  return (
    <div className="pokemon-list">
      {/* 渲染卡片时把共享状态和更新方法传下去,替换成你实际的接口地址列表循环渲染即可 */}
      <PokemonCard 
        url="宝可梦接口地址1" 
        capturedPkm={capturedPkm}
        setCapturedPkm={setCapturedPkm}
      />
      <PokemonCard 
        url="宝可梦接口地址2" 
        capturedPkm={capturedPkm}
        setCapturedPkm={setCapturedPkm}
      />
    </div>
  )
}

export default PokemonList

修改后的PokemonCard组件

import React, { useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import PokemonIcon from './PokemonIcon';

// 接收父组件传的capturedPkm和setCapturedPkm
const PokemonCard = (props) => {
    const { url, capturedPkm, setCapturedPkm } = props
    const [pokemonCard, setPokemonCard] = useState({})
    // 动态判断当前宝可梦是否被捕获,不需要单独存label状态
    const isCaptured = capturedPkm.includes(pokemonCard.name)

    const toggleCaptured = (checked) => {
        if (checked) {
            // 函数式更新保证拿到最新的状态值,追加新宝可梦名称
            setCapturedPkm(prev => [...prev, pokemonCard.name])
        } else {
            // 取消选中时过滤掉当前宝可梦
            setCapturedPkm(prev => prev.filter(name => name !== pokemonCard.name))
        }
    }

    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, capturedPkm }}
                    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'
                            checked={isCaptured}
                            onChange={(e) => toggleCaptured(e.target.checked)}
                        />
                        <span style={{ marginLeft: 8, cursor: 'pointer' }}>
                            {isCaptured ? '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 09:27:02