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

React中API数据获取与选中项同步至表格的问题排查

问题描述

我有两个表格:全部产品表与已选产品表,当前使用jsonplaceholder/users接口进行演示。全部产品表配备搜索栏,可展示API返回的所有数据。全部产品表的每条数据均带有复选框,我希望勾选复选框时,对应数据能同步显示在已选产品表中。但checked状态的useState()始终返回空数组[],无法获取选中数据,导致已选产品表无法正常展示选中项。

useState钩子及函数代码

const [checked, setChecked] = useState([])
const [input, setInput] = useState("");
const [results, setResults] = useState([])
const [apiData, setApiData] = useState([]);

const handleAPI = async (url) => {
    try {
        let fetchData = await fetch("https://jsonplaceholder.typicode.com/users");
        let data = await fetchData.json();
        setApiData(data);

        const result = data.filter((user) => {
            return (
                url &&
                user &&
                user.name &&
                user.name.toLowerCase().includes(url)

            )
        })
        // console.log(result)
        setResults(result)
    } catch (err) {
        console.log(err)
    }
}

useEffect(() => {
    handleAPI()
}, [])


const handleSearch = (value) => {
    setInput(value);
    handleAPI(value);
}

const handleCheck = (id) => {
    setChecked(prevState => [...prevState, id])
    console.log(checked)
}

const handleDelete = (product) => {
    const deleteData = checked.filter((item) => 
        item.id !== product.id);
    setChecked(deleteData)       

}

全部产品表代码

<table className='w-full h-max'>
 <tr>
   <th className='flex items-center gap-3 text-left' >
     <img src={dashboardCheckbox} alt="checkbox" />
     <p className='text-xs text-[#667085] font-medium'>Product</p>
   </th>

   <th className='text-left'>
     <p className='text-xs text-[#667085] font-medium'>Price</p>
   </th>

   <th className='text-left'>
      <p className='text-xs text-[#667085] font-medium'>Status</p>
   </th>
 </tr>

 {results.map((res, id) => (
   <tr key={id} >
     <td className='flex items-center py-4'>
       <input onClick={(id) => handleCheck(res.id)} type="checkbox" className='w-[19px] h-[19px] mr-[0.8rem] cursor-pointer' />
       <div className='flex items-center gap-3'>
         <img src={avatar} alt="" />
       <div>
       <div className='text-sm font-medium'>{res.name}</div>
         <div className='text-sm text-[#667085]'>{res.company.name}</div>
       </div>
      </div>
     </td>
     <td className='text-sm'>₹160</td>
     <td className='text-xs'>&#8226; Not Available</td>
    </tr>
))}
</table>

已选产品表代码

<table className='w-full h-max'>
  <tr className=''>
    <th className='flex items-center gap-3 text-left' >
     <p className='text-xs text-[#667085] font-medium'>Product</p>
    </th>

    <th className='text-left'>
      <p className='text-xs text-[#667085] font-medium'>Price</p>
    </th>

   </tr>

   {checked.map((r, id) => (
     <tr key={id}>
       <td className='flex items-center py-4'>
       <div className='flex items-center gap-3'>
         <img src={avatar} alt="" />
        <div>
         <div className='text-sm font-medium'>{r.name}</div>
         <div className='text-sm text-[#667085]'>seller</div>
        </div>
       </div>
      </td>

      <td className='text-sm'>₹160</td>
      <td className='text-sm cursor-pointer' onClick={(id) => handleDelete(id)}>Delete</td>
    </tr>
                                    
 ))}
</table>

问题分析与修复方案

核心问题

  1. checked存储id数组,但已选表直接遍历id取属性:checked里存的是用户id,不是完整对象,r.name会报错,导致渲染失败。
  2. handleCheck仅处理勾选,未支持取消:只能往数组加id,无法移除,逻辑不完整。
  3. checkbox未绑定状态:勾选后视觉上不会保持选中,体验差。
  4. handleDelete逻辑错误:传入的是id,但代码用item.id !== product.id判断,而item本身是id,条件永远成立,删除无效。
  5. 初始搜索逻辑缺陷:url为空时,过滤条件url && ...返回false,导致初始results为空数组。

修复步骤

1. 修复handleCheck,支持勾选/取消勾选

const handleCheck = (id) => {
  setChecked(prevState => {
    // 存在则移除,不存在则添加
    return prevState.includes(id)
      ? prevState.filter(item => item !== id)
      : [...prevState, id];
  });
}

2. 给checkbox绑定状态,替换onClick为onChange

<input 
  type="checkbox" 
  className='w-[19px] h-[19px] mr-[0.8rem] cursor-pointer'
  checked={checked.includes(res.id)}
  onChange={() => handleCheck(res.id)}
/>

3. 修复已选产品表渲染逻辑,从apiData匹配完整数据

{// 根据checked中的id筛选出完整用户数据
apiData.filter(user => checked.includes(user.id)).map((r) => (
  <tr key={r.id}> {/* 用用户id作为key,更稳定 */}
    <td className='flex items-center py-4'>
      <div className='flex items-center gap-3'>
        <img src={avatar} alt="" />
        <div>
          <div className='text-sm font-medium'>{r.name}</div>
          <div className='text-sm text-[#667085]'>{r.company.name}</div>
        </div>
      </div>
    </td>
    <td className='text-sm'>₹160</td>
    <td className='text-sm cursor-pointer' onClick={() => handleDelete(r.id)}>Delete</td>
  </tr>
))}

4. 修复handleDelete函数

const handleDelete = (id) => {
  setChecked(prevState => prevState.filter(item => item !== id));
}

5. 优化handleAPI搜索逻辑,支持初始加载所有数据

const handleAPI = async (url) => {
  try {
    let fetchData = await fetch("https://jsonplaceholder.typicode.com/users");
    let data = await fetchData.json();
    setApiData(data);

    const result = data.filter((user) => {
      // 空搜索返回所有数据,否则按名称模糊匹配(不区分大小写)
      if (!url) return true;
      return user?.name?.toLowerCase().includes(url.toLowerCase());
    });
    setResults(result);
  } catch (err) {
    console.log(err);
  }
}

内容的提问来源于stack exchange,提问作者SE1822

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 12:48:11