你的组件通常需要根据不同的条件显示不同的内容。在 React 中,你可以使用 JavaScript 语法(如 if 语句、&&? : 运算符)有条件地渲染 JSX。

你将学习

  • 如何根据条件返回不同的 JSX
  • 如何有条件地包含或排除一段 JSX
  • 在 React 代码库中常见的条件语法快捷方式

有条件地返回 JSX

假设你有一个 PackingList 组件,它渲染了几个 Item,这些 Item 可以标记为已打包或未打包

function Item({ name, isPacked }) {
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

请注意,某些 Item 组件的 isPacked 属性设置为 true 而不是 false。你希望在 isPacked={true} 的情况下为已打包的商品添加一个复选标记 (✔)。

你可以将其写成一个 if/else 语句,如下所示

if (isPacked) {
return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

如果 isPacked 属性为 true,则此代码 返回不同的 JSX 树。 通过此更改,某些商品的末尾会显示一个复选标记

function Item({ name, isPacked }) {
  if (isPacked) {
    return <li className="item">{name}</li>;
  }
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

尝试编辑在任何一种情况下返回的内容,看看结果如何变化!

请注意,你是如何使用 JavaScript 的 ifreturn 语句创建分支逻辑的。在 React 中,控制流(如条件)由 JavaScript 处理。

使用 null 有条件地不返回任何内容

在某些情况下,你根本不想渲染任何内容。例如,假设你根本不想显示已打包的商品。组件必须返回一些内容。在这种情况下,你可以返回 null

if (isPacked) {
return null;
}
return <li className="item">{name}</li>;

如果 isPacked 为 true,则组件将不返回任何内容,null。否则,它将返回要渲染的 JSX。

function Item({ name, isPacked }) {
  if (isPacked) {
    return null;
  }
  return <li className="item">{name}</li>;
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

在实践中,从组件返回 null 并不常见,因为它可能会让试图渲染它的开发者感到惊讶。更常见的情况是,你会在父组件的 JSX 中有条件地包含或排除该组件。以下是如何做到这一点!

有条件地包含 JSX

在前面的示例中,你控制了组件将返回哪个(如果有的话!)JSX 树。你可能已经注意到渲染输出中有一些重复

<li className="item">{name}</li>

<li className="item">{name}</li>

非常相似

if (isPacked) {
return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

这两个条件分支都返回 <li className="item">...</li>

虽然这种重复是无害的,但它可能会使你的代码更难维护。如果你想更改 className怎么办?你必须在代码的两个地方进行更改!在这种情况下,你可以有条件地包含一小段 JSX,使你的代码更加 DRY。

JavaScript 有一种简洁的语法来编写条件表达式,即 条件运算符 或“三元运算符”。

您可以使用以下代码代替:

if (isPacked) {
return <li className="item">{name}</li>;
}
return <li className="item">{name}</li>;

您可以编写以下代码:

return (
<li className="item">
{isPacked ? name + ' ✔' : name}
</li>
);

您可以将其解读为 “如果 isPacked 为 true,则 (?) 渲染 name + ' ✔',否则 (:) 渲染 name

深入探讨

这两个例子完全等价吗?

如果您有面向对象编程的背景,您可能会认为上面两个例子略有不同,因为其中一个例子可能会创建两个不同的 <li> “实例”。但 JSX 元素不是“实例”,因为它们不保存任何内部状态,也不是真正的 DOM 节点。它们是轻量级的描述,就像蓝图一样。因此,这两个例子实际上是完全等价的。保留和重置状态 详细介绍了它是如何工作的。

现在假设您想将已完成项目的文本包装到另一个 HTML 标签中,例如 <del> 以将其删除线。您可以添加更多换行符和括号,以便更容易地在每种情况下嵌套更多 JSX

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {isPacked ? (
        <del>
          {name + ' ✔'}
        </del>
      ) : (
        name
      )}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

这种风格适用于简单的条件,但要适度使用。如果您的组件因嵌套过多的条件标记而变得混乱,请考虑提取子组件以清理代码。在 React 中,标记是代码的一部分,因此您可以使用变量和函数等工具来整理复杂的表达式。

逻辑与运算符 (&&)

您会遇到的另一个常见快捷方式是 JavaScript 逻辑与 (&&) 运算符。 在 React 组件中,当您希望在条件为 true 时渲染一些 JSX,否则不渲染任何内容时,它经常出现。使用 &&,您可以有条件地渲染复选标记,仅当 isPackedtrue

return (
<li className="item">
{name} {isPacked && '✔'}
</li>
);

您可以将其解读为 “如果 isPacked,则 (&&) 渲染复选标记,否则不渲染任何内容”

以下是实际操作示例

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✔'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

JavaScript && 表达式 如果左侧(我们的条件)为 true,则返回其右侧的值(在我们的例子中为复选标记)。但如果条件为 false,则整个表达式变为 false。React 将 false 视为 JSX 树中的一个“空洞”,就像 nullundefined 一样,并且不会在其位置渲染任何内容。

陷阱

不要将数字放在 && 的左侧。

为了测试条件,JavaScript 会自动将左侧转换为布尔值。但是,如果左侧为 0,则整个表达式将获得该值 (0),并且 React 将愉快地渲染 0 而不是什么也不渲染。

例如,一个常见的错误是编写如下代码:messageCount && <p>新消息</p>。很容易假设当 messageCount0 时它什么也不渲染,但它实际上渲染了 0 本身!

要解决此问题,请将左侧设为布尔值:messageCount > 0 && <p>新消息</p>

有条件地将 JSX 分配给变量

当快捷方式妨碍编写清晰的代码时,尝试使用 if 语句和一个变量。您可以重新分配使用 let 定义的变量,因此首先提供您要显示的默认内容,即名称

let itemContent = name;

使用 if 语句在 isPackedtrue 时将 JSX 表达式重新分配给 itemContent

if (isPacked) {
itemContent = name + " ✔";
}

花括号打开了“通往 JavaScript 世界的窗口”。 使用花括号将变量嵌入返回的 JSX 树中,将先前计算的表达式嵌套在 JSX 中

<li className="item">
{itemContent}
</li>

这种风格是最冗长的,但也是最灵活的。以下是实际操作示例

function Item({ name, isPacked }) {
  let itemContent = name;
  if (isPacked) {
    itemContent = name + " ✔";
  }
  return (
    <li className="item">
      {itemContent}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

和之前一样,这不仅适用于文本,也适用于任意 JSX。

function Item({ name, isPacked }) {
  let itemContent = name;
  if (isPacked) {
    itemContent = (
      <del>
        {name + " ✔"}
      </del>
    );
  }
  return (
    <li className="item">
      {itemContent}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}

如果您不熟悉 JavaScript,那么这种多样化的风格一开始可能会让您不知所措。但是,学习它们将帮助您读写任何 JavaScript 代码,而不仅仅是 React 组件!首先选择您喜欢的一种,然后如果您忘记了其他风格如何工作,请再次查阅本参考。

回顾

  • 在 React 中,您可以使用 JavaScript 控制分支逻辑。
  • 您可以使用 if 语句有条件地返回 JSX 表达式。
  • 您可以有条件地将一些 JSX 保存到变量中,然后使用花括号将其包含在其他 JSX 中。
  • 在 JSX 中,{cond ? <A /> : <B />} 表示 _“如果 cond,则渲染 <A />,否则渲染 <B />”_。
  • 在 JSX 中,{cond && <A />} 表示 _“如果 cond,则渲染 <A />,否则不渲染任何内容”_。
  • 快捷方式很常见,但如果您更喜欢普通的 if,则不必使用它们。

挑战 1 3:
使用 ? : 显示未完成项目的图标

如果 isPacked 不是 true,请使用条件运算符 (cond ? a : b) 渲染 ❌。

function Item({ name, isPacked }) {
  return (
    <li className="item">
      {name} {isPacked && '✔'}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="Space suit" 
        />
        <Item 
          isPacked={true} 
          name="Helmet with a golden leaf" 
        />
        <Item 
          isPacked={false} 
          name="Photo of Tam" 
        />
      </ul>
    </section>
  );
}