Skip to content
第 147 / 250 章前端⏱ 10 分钟阅读

第 147 章:条件渲染与列表渲染

学习目标

  • 掌握 4 种条件渲染写法
  • 理解列表 key 的作用
  • 学会稳定 ID 与 key 的选择
  • 处理空状态和加载状态

一、条件渲染 4 种方式

1.1 if 语句(在 return 之前)

tsx
function Greeting({ user }: { user: User | null }) {
  if (!user) {
    return <p>请登录</p>;
  }
  return <p>欢迎,{user.name}!</p>;
}

1.2 三元运算符

tsx
{isLoggedIn ? <UserMenu /> : <LoginButton />}
{user ? <Avatar src={user.avatar} /> : <DefaultAvatar />}

1.3 逻辑与 &&

tsx
{isAdmin && <AdminPanel />}
{error && <ErrorMessage msg={error} />}
{count > 0 && <Badge count={count} />}

1.4 IIFE / 立即函数

tsx
{(() => {
  switch (status) {
    case 'loading': return <Spinner />;
    case 'error':   return <Error />;
    case 'success': return <Content data={data} />;
    default:        return null;
  }
})()}

二、列表渲染

2.1 基础 map

tsx
const fruits = ['苹果', '香蕉', '橙子'];

return (
  <ul>
    {fruits.map((fruit, index) => (
      <li key={fruit}>{fruit}</li>
    ))}
  </ul>
);

2.2 对象列表

tsx
interface User { id: number; name: string; email: string; }
const users: User[] = [
  { id: 1, name: 'Tom', email: 'tom@x.com' },
  { id: 2, name: 'Jerry', email: 'jerry@x.com' },
];

return (
  <ul>
    {users.map(user => (
      <li key={user.id}>
        {user.name} - {user.email}
      </li>
    ))}
  </ul>
);

2.3 表格

tsx
<table>
  <thead>
    <tr><th>ID</th><th>姓名</th><th>邮箱</th></tr>
  </thead>
  <tbody>
    {users.map(user => (
      <tr key={user.id}>
        <td>{user.id}</td>
        <td>{user.name}</td>
        <td>{user.email}</td>
      </tr>
    ))}
  </tbody>
</table>

三、Key 的选择

3.1 为什么需要 key

React 用 key 识别哪些元素变了、新增了、删除了,帮助高效 diff。

tsx
// ❌ 没 key,React 警告
{items.map(item => <li>{item}</li>)}

// ✅ 有 key
{items.map(item => <li key={item.id}>{item.name}</li>)}

3.2 稳定的 ID

tsx
// ✅ 最佳:用业务 ID
items.map(item => <li key={item.id}>...</li>)

// ⚠️ 可接受:用 index(只在静态列表)
items.map((item, i) => <li key={i}>...</li>)

// ❌ 错误:用随机数
items.map(item => <li key={Math.random()}>...</li>)

3.3 列表会重排时不要用 index

tsx
// ❌ 重排后 key 不变,React 错乱
const [list, setList] = useState(['A', 'B', 'C']);
// 删除 A 后,key=0 实际是 B,出错
{list.map((item, i) => <li key={i}>{item}</li>)}

四、空状态与加载状态

tsx
function UserList() {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<Error | null>(null);

  if (loading) return <Spinner />;
  if (error) return <ErrorMessage error={error} />;
  if (users.length === 0) return <Empty message="暂无用户" />;

  return (
    <ul>
      {users.map(user => <UserItem key={user.id} user={user} />)}
    </ul>
  );
}

五、过滤与排序

tsx
function UserList({ users }: { users: User[] }) {
  const [keyword, setKeyword] = useState('');

  const filtered = users.filter(u =>
    u.name.toLowerCase().includes(keyword.toLowerCase())
  );

  return (
    <>
      <input
        placeholder="搜索"
        value={keyword}
        onChange={e => setKeyword(e.target.value)}
      />
      <ul>
        {filtered.map(user => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </>
  );
}

六、列表项组件

tsx
// 列表项单独封装
function TodoItem({ todo, onToggle, onDelete }: TodoItemProps) {
  return (
    <li className="flex items-center gap-2">
      <input
        type="checkbox"
        checked={todo.done}
        onChange={() => onToggle(todo.id)}
      />
      <span className={todo.done ? 'line-through' : ''}>
        {todo.text}
      </span>
      <button onClick={() => onDelete(todo.id)}>删除</button>
    </li>
  );
}

// 列表
function TodoList({ todos, onToggle, onDelete }: TodoListProps) {
  return (
    <ul>
      {todos.map(todo => (
        <TodoItem
          key={todo.id}
          todo={todo}
          onToggle={onToggle}
          onDelete={onDelete}
        />
      ))}
    </ul>
  );
}

七、嵌套列表

tsx
interface Category { id: number; name: string; items: Item[]; }

{categories.map(cat => (
  <div key={cat.id}>
    <h3>{cat.name}</h3>
    <ul>
      {cat.items.map(item => (
        <li key={item.id}>{item.name}</li>
      ))}
    </ul>
  </div>
))}

八、本章小结

概念关键
条件渲染if / 三元 / && / IIFE
列表map + 稳定 key
key 选 ID不要用 index 或随机数
状态分支loading / error / empty / success

动手练习

  1. 写一个待办列表,支持添加、勾选、删除、过滤(全部/已完成/未完成)
  2. 写一个用户列表,支持搜索、排序、加载状态
  3. 写一个评论树,嵌套渲染回复

推荐阅读


下一章:第 148 章:事件处理

本站基于 VitePress 构建 · 由 Codebook 团队维护