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

通过ID数组渲染对象数组元素:TypeScript报错与最佳实践

问题解答

1. 问题出在哪里?

你的两段代码都存在逻辑错误,导致TypeScript报错:

第一段代码的问题

你完全误用了filter的作用:filter是用来筛选数组元素的,回调函数必须返回布尔值,但你在回调里返回了<li>{book.title}</li>这种JSX元素,根本起不到筛选作用。同时map函数内部没有返回filter的结果,最终renderBooks()返回的是全为undefined的数组,React无法渲染;就算补上return,filter返回数组,map会生成二维数组,也不符合<ul>子元素的要求。

第二段简化代码的问题

renderArray()返回的是fakeObj类型的对象数组,但<ul>的子元素必须是有效的React元素(比如<li>),直接渲染普通对象会触发TypeScript类型检查错误——React无法识别并渲染普通对象。

2. 存储ID还是完整书籍?

推荐存储书籍ID,原因如下:

  • 数据一致性:如果目录里的书籍信息(如标题、作者)更新,通过ID匹配能自动获取最新数据,无需额外维护收藏列表里的冗余数据。
  • 状态体积小:存储ID数组比存储完整书籍对象节省内存,尤其当书籍数量较多时。
  • 符合Redux最佳实践:Redux官方推荐使用归一化状态结构,将不同实体(如书籍、用户收藏)分开存储,通过ID关联,避免数据冗余。

如果存储完整书籍,会带来状态冗余、数据同步麻烦的问题——目录书籍更新后,需手动同步收藏列表里的对应数据,增加代码复杂度。

3. 存储ID时渲染列表的最佳实践

核心思路是先把书籍列表转成以ID为键的查找表,通过ID查找书籍的效率为O(1),远高于每次filter的O(n),同时避免嵌套数组问题。

修正后的第一段代码

const Wishlist = () => {
  const dispatch = useAppDispatch();
  const wishlist = useAppSelector(state => state.user.userInfo.wishlist);
  const bookList = useAppSelector(state => state.catalogue.list);

  // 把书籍列表转成查找表:{ [bookId]: book },用useMemo缓存避免重复生成
  const bookLookup = useMemo(() => {
    return bookList.reduce((acc, book) => {
      acc[book.id] = book;
      return acc;
    }, {} as Record<string, typeof bookList[0]>);
  }, [bookList]);

  return (
    <ul>
      {wishlist.map(wishId => {
        const book = bookLookup[wishId];
        // 处理书籍不存在的边界情况(如ID无效或书籍已删除)
        if (!book) return null;
        return <li key={wishId}>{book.title}</li>;
      })}
    </ul>
  );
};

export default Wishlist;

修正后的第二段简化代码

interface FakeObj {
  id: string;
  name: string;
}

const fakeIds = ['5', '12', '878'];
const fakeList: FakeObj[] = [
  { id: '334', name: 'Tony' },
  { id: '5', name: 'Milly' },
  { id: '12', name: 'Jack' },
  { id: '7', name: 'Stew' },
  { id: '878', name: 'Mark' },
  { id: '87998', name: 'Greg' }
];

// 转成查找表
const objLookup = fakeList.reduce((acc, obj) => {
  acc[obj.id] = obj;
  return acc;
}, {} as Record<string, FakeObj>);

return (
  <ul>
    {fakeIds.map(id => {
      const obj = objLookup[id];
      if (!obj) return null;
      return <li key={id}>{obj.name}</li>;
    })}
  </ul>
);

关键优化点:

  • 用useMemo缓存查找表,仅在书籍列表变化时重新生成,避免不必要的渲染
  • 给每个<li>添加唯一key(用书籍ID即可),符合React列表渲染要求
  • 处理书籍不存在的边界情况,避免渲染错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 08:35:32