
本文详解如何使用 React 的 useState 和 useEffect 从 JSONPlaceholder 获取用户数据,并通过复选框实现“全量列表 ↔ 已选列表”的双向同步,解决常见状态更新延迟、数据映射错误及删除逻辑失效等问题。
本文详解如何使用 react 的 `usestate` 和 `useeffect` 从 jsonplaceholder 获取用户数据,并通过复选框实现“全量列表 ↔ 已选列表”的双向同步,解决常见状态更新延迟、数据映射错误及删除逻辑失效等问题。
在 React 应用中,动态展示并交互式管理远程 API 数据(如产品列表)是高频需求。本教程以“所有产品表 ↔ 已选产品表”双表格场景为例,完整演示从数据获取、搜索过滤、复选状态维护到跨表渲染的全流程实现。
✅ 核心问题与修复要点
原代码中 checked 状态始终为空数组,根本原因有三:
-
状态更新异步性误用:
console.log(checked)在setChecked()后立即执行,读取的是旧状态(React 状态更新是异步批处理的); -
ID 存储 vs 数据存储混淆:
setChecked([...prevState, id])仅保存了 ID,但“已选表格”渲染时却尝试直接访问r.name—— 此时r是数字 ID,而非对象,导致undefined错误; -
删除逻辑错误:
handleDelete(product)接收的是索引id,却误当作对象解构product.id,且未基于id过滤数组。
✅ 正确实现方案
我们采用「ID 列表 + 实时查找」策略:checked 仅安全存储选中项的 id(轻量、可去重、易序列化),渲染时通过 data.find(item => item.id === id) 动态获取对应完整数据。
? 关键代码优化说明
// ✅ 正确的复选处理:支持勾选/取消勾选
const handleCheck = (id) => {
if (checked.includes(id)) {
// 已选中 → 取消:过滤掉该 id
setChecked(prev => prev.filter(itemId => itemId !== id));
} else {
// 未选中 → 勾选:追加该 id
setChecked(prev => [...prev, id]);
}
};
// ✅ 正确的删除处理:接收 id,精准过滤
const handleDelete = (id) => {
setChecked(prev => prev.filter(itemId => itemId !== id));
};? 渲染已选表格(关键!)
{checked.map((id, idx) => {
const selectedUser = data.find(user => user.id === id); // ✅ 用 id 查找原始数据
return (
<tr key={idx}>
<td className="flex items-center py-4">
<div className="flex items-center gap-3">
<div className="text-sm font-medium">{selectedUser?.name}</div>
<div className="text-sm text-[#667085]">{selectedUser?.company?.name}</div>
</div>
</td>
<td className="text-sm">₹160</td>
<td>
<button
onClick={() => handleDelete(id)}
className="text-red-600 hover:underline"
>
删除
</button>
</td>
</tr>
);
})}⚠️ 注意:使用可选链
?.防止selectedUser为undefined时崩溃(例如数据尚未加载完成或 ID 无效)。
✅ 增强体验:加载状态与健壮性
为提升用户体验,添加加载骨架(Spinner):
if (spinner) {
return (
<div className="flex justify-center items-center h-96">
<CircularProgress size={40} />
</div>
);
}同时,推荐使用 axios 替代原生 fetch(自动 JSON 解析、更简洁的错误处理),并在 useEffect 中严格依赖数组 [] 确保仅初始化加载一次。
✅ 完整最佳实践总结
| 环节 | 推荐做法 |
|---|---|
| 数据获取 | 使用 axios.get() + useEffect([], ...) 初始化;设置 loading 状态控制 UI |
| 搜索过滤 | 不直接修改 apiData,而是基于其生成 filteredData 渲染 All Table(避免重复请求) |
| 状态管理 |
checked: number[] 存 ID;渲染时 find() 拉取完整对象 —— 分离关注点,避免状态冗余 |
| 事件绑定 | 复选框用 onChange + checked={checked.includes(id)} 实现受控组件,确保视觉与状态一致 |
| 错误防御 | 渲染时使用 ?. 和默认值(如 {selectedUser?.name || '未知用户'}) |
通过以上结构化实现,你将获得一个响应迅速、逻辑清晰、易于扩展的 React 多选数据管理模块——无论是对接真实商品 API,还是集成至复杂后台系统,均具备坚实基础。


















