Dark Dwarf Blog background

React 渲染

React 渲染

1. 列表渲染

在应用中,我们经常需要渲染一个项目列表 (Rendering Lists),比如一个待办事项清单、用户列表或文章列表。直接在 JSX 中硬编码列表项是不现实的。

a.a. 使用 map() 方法渲染列表

处理动态列表的标准做法是使用 JavaScript 的 Array.prototype.map() 方法。我们可以在 JSX 的花括号 {} 中,将一个数据数组转换为一个 React 元素数组。

function ShoppingList() {
  const products = [
    { id: 1, title: 'Cabbage' },
    { id: 2, title: 'Garlic' },
    { id: 3, title: 'Apple' },
  ];

  const listItems = products.map(product =>
    // 对于列表中的每一项,我们都创建一个 <li> 元素
    <li key={product.id}>
      {product.title}
    </li>
  );

  return (
    <ul>{listItems}</ul>
  );
}

React 会自动渲染 listItems 数组中的所有 <li> 元素。

这里的 key 属性之后会详细讲解。

c.c. 将列表渲染抽取为组件

为了更好的代码组织和复用,我们可以将列表项的渲染逻辑抽取到一个单独的组件中。

// ListItem 组件负责渲染单个列表项
function ListItem({ title }) {
  return <li>{title}</li>;
}

function ShoppingList() {
  const products = [
    { id: 1, title: 'Cabbage' },
    { id: 2, title: 'Garlic' },
  ];

  return (
    <ul>
      {products.map(product => (
        // key 应该放在 map 循环内部的最外层元素上
        <ListItem key={product.id} title={product.title} />
      ))}
    </ul>
  );
}

2. 条件渲染

在 React 中,我们可以根据组件的状态或属性来决定渲染哪个部分。这被称为条件渲染 (Conditional Rendering)。

a.a. 使用 if 语句 (守卫子句)

在组件的顶层逻辑中,我们可以使用 if 语句来提前返回(也称为“守卫子句”),这对于处理加载状态或空状态非常有用。

function ItemList({ items }) {
  // 条件 1: 数据还未加载完成
  if (!items) {
    return <div>Loading...</div>;
  }

  // 条件 2: 数据为空
  if (items.length === 0) {
    return <div>No items found.</div>;
  }

  // 正常渲染
  return (
    <ul>
      {items.map(item => <li key={item.id}>{item.name}</li>)}
    </ul>
  );
}

这种模式让代码的可读性非常高,因为它清晰地处理了各种边界情况。

b.b. 使用三元运算符 ? :

对于简单的“二选一”场景,三元运算符是在 JSX 内部进行条件渲染的最佳方式。

function PackingList({ items }) {
  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>
          {item.name} 
          {/* 如果 item 已打包,则显示 ✔,否则什么也不显示 */}
          {item.isPacked ? '✔' : null}
        </li>
      ))}
    </ul>
  );
}

返回 null 会告诉 React 在该位置不渲染任何东西。

c.c. 使用逻辑与 && 运算符

当我们只想在某个条件为真时渲染一个元素,否则什么都不渲染时,逻辑与 && 运算符提供了一种更简洁的写法。

function Mailbox({ unreadMessages }) {
  return (
    <div>
      <h1>Hello!</h1>
      {unreadMessages.length > 0 &&
        <h2>
          You have {unreadMessages.length} unread messages.
        </h2>
      }
    </div>
  );
}

它的工作原理是:在 JavaScript 中,true && expression 总是返回 expression,而 false && expression 总是返回 false。React 不会渲染布尔值 false,因此达到了预期效果。

注意:&& 左侧的表达式不应该是数字。如果 unreadMessages.length 的值是 00 && <SomeComponent /> 会返回 0,React 会将数字 0 渲染到屏幕上,而不是什么都不渲染。因此,请确保 && 左侧始终是一个布尔值,例如 unreadMessages.length > 0

d.d. 推荐的实践方式

  • 多个互斥的返回 -> if 语句: 当我们需要根据不同的条件返回完全不同的组件结构时(如加载、错误、空、成功),使用 if 守卫子句模式。
  • 简单的二选一 -> 三元运算符 ? :: 当我们需要在两个简单的 JSX 表达式之间切换时,这是最清晰的选择。
  • “有或无”的场景 -> 逻辑与 &&: 当我们只想在某个条件满足时才渲染某个元素时,&& 是最简洁的。
  • 复杂的逻辑 -> 抽取变量: 如果条件逻辑变得复杂,最好在 return 语句之前,用 let 声明一个变量,通过 if/else 逻辑给它赋不同的 JSX 值,最后在 return 中渲染这个变量。这可以避免 JSX 内部过于混乱。
function ComplexComponent({ status }) {
  let content;
  if (status === 'loading') {
    content = <div>Loading...</div>;
  } else if (status === 'error') {
    content = <div>Error loading data.</div>;
  } else {
    content = <DataTable />;
  }

  return (
    <div>
      <h1>Dashboard</h1>
      {content}
    </div>
  );
}