条件渲染 | Conditional Rendering

Your components will often need to display different things depending on different conditions. In React, you can conditionally render JSX using JavaScript syntax like if statements, &&, and ? : operators. 通常你的组件会需要根据不同的情况显示不同的内容。在 React 中,你可以通过使用 JavaScript 的 if 语句、&&? : 运算符来选择性地渲染 JSX。

你将会学习到

  • How to return different JSX depending on a condition
  • 如何根据不同条件返回不同的 JSX
  • How to conditionally include or exclude a piece of JSX
  • 如何根据不同条件包含或者去掉部分 JSX
  • Common conditional syntax shortcuts you’ll encounter in React codebases
  • 一些你会在 React 代码库里遇到的常用的条件语法快捷表达式

条件返回 JSX | Conditionally returning JSX

Let’s say you have a PackingList component rendering several Items, which can be marked as packed or not: 假设有一个 PackingList 组件,里面渲染多个 Item 组件,每个物品可标记为打包与否:

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

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride 的行李清单</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="宇航服" 
        />
        <Item 
          isPacked={true} 
          name="带金箔的头盔" 
        />
        <Item 
          isPacked={false} 
          name="Tam 的照片" 
        />
      </ul>
    </section>
  );
}

Notice that some of the Item components have their isPacked prop set to true instead of false. You want to add a checkmark (✅) to packed items if isPacked={true}. 需要注意的是,有些 Item 组件的 isPacked 属性是被设为 true 而不是 false。你可以在那些满足 isPacked={true} 条件的物品旁加上一个勾选符号(✅)。

You can write this as an if/else statement like so: 你可以用 if/else 语句 去判断:

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

If the isPacked prop is true, this code returns a different JSX tree. With this change, some of the items get a checkmark at the end: 如果 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 的行李清单</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="宇航服" 
        />
        <Item 
          isPacked={true} 
          name="带金箔的头盔" 
        />
        <Item 
          isPacked={false} 
          name="Tam 的照片" 
        />
      </ul>
    </section>
  );
}

Try editing what gets returned in either case, and see how the result changes! 动手尝试一下,看看各种情况会出现什么不同的结果!

Notice how you’re creating branching logic with JavaScript’s if and return statements. In React, control flow (like conditions) is handled by JavaScript. 留意这里你是怎么使用 JavaScript 的 ifreturn 语句来写分支逻辑。在 React 中,是由 JavaScript 来处理控制流的(比如条件)。

选择性地返回 null | Conditionally returning nothing with null

In some situations, you won’t want to render anything at all. For example, say you don’t want to show packed items at all. A component must return something. In this case, you can return null: 在一些情况下,你不想有任何东西进行渲染。比如,你不想显示已经打包好的物品。但一个组件必须返回一些东西。这种情况下,你可以直接返回 null

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

If isPacked is true, the component will return nothing, null. Otherwise, it will return JSX to render. 如果组件的 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 的行李清单</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="宇航服" 
        />
        <Item 
          isPacked={true} 
          name="带金箔的头盔" 
        />
        <Item 
          isPacked={false} 
          name="Tam 的照片" 
        />
      </ul>
    </section>
  );
}

In practice, returning null from a component isn’t common because it might surprise a developer trying to render it. More often, you would conditionally include or exclude the component in the parent component’s JSX. Here’s how to do that! 实际上,在组件里返回 null 并不常见,因为这样会让想使用它的开发者感觉奇怪。通常情况下,你可以在父组件里选择是否要渲染该组件。让我们接着往下看吧!

选择性地包含 JSX | Conditionally including JSX

In the previous example, you controlled which (if any!) JSX tree would be returned by the component. You may already have noticed some duplication in the render output: 在之前的例子里,你在组件内部控制哪些 JSX 树(如果有的话!)会返回。你可能已经发现了在渲染输出里会有一些重复的内容:

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

is very similar to 和下面的写法很像:

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

Both of the conditional branches return <li className="item">...</li>: 两个条件分支都会返回 <li className="item">...</li>

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

While this duplication isn’t harmful, it could make your code harder to maintain. What if you want to change the className? You’d have to do it in two places in your code! In such a situation, you could conditionally include a little JSX to make your code more DRY. 虽然这些重复的内容没什么害处,但这样可能会导致你的代码更难维护。比如你想更改 className?你就需要修改两个地方!针对这种情况,你可以通过选择性地包含一小段 JSX 来让你的代码更加 DRY

三目运算符(? :)| Conditional (ternary) operator (? :)

JavaScript has a compact syntax for writing a conditional expression — the conditional operator or “ternary operator”. JavaScript 有一种紧凑型语法来实现条件判断表达式——条件运算符 又称“三目运算符”。

Instead of this: 除了这样:

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

You can write this: 你还可以这样实现:

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

You can read it as “if isPacked is true, then (?) render name + ' ✅', otherwise (:) render name. 你可以认为,“如果 isPacked 为 true 时,则(?)渲染 name + ' ✅',否则(:)渲染 name。”

深入探讨

两个例子完全一样吗?| Are these two examples fully equivalent?

If you’re coming from an object-oriented programming background, you might assume that the two examples above are subtly different because one of them may create two different “instances” of <li>. But JSX elements aren’t “instances” because they don’t hold any internal state and aren’t real DOM nodes. They’re lightweight descriptions, like blueprints. So these two examples, in fact, are completely equivalent. Preserving and Resetting State goes into detail about how this works. 如果你之前是习惯面向对象开发的,你可能会认为上面的两个例子略有不同,因为其中一个可能会创建两个不同的 <li> “实例”。但 JSX 元素不是“实例”,因为它们没有内部状态也不是真实的 DOM 节点。它们只是一些简单的描述,就像图纸一样。所以上面这两个例子事实上是完全相同的。在 状态的保持和重置 里会深入探讨其原因。

Now let’s say you want to wrap the completed item’s text into another HTML tag, like <del> to strike it out. You can add even more newlines and parentheses so that it’s easier to nest more JSX in each of the cases: 现在,假如你想将对应物品的文本放到另一个 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 的行李清单</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="宇航服" 
        />
        <Item 
          isPacked={true} 
          name="带金箔的头盔" 
        />
        <Item 
          isPacked={false} 
          name="Tam 的照片" 
        />
      </ul>
    </section>
  );
}

This style works well for simple conditions, but use it in moderation. If your components get messy with too much nested conditional markup, consider extracting child components to clean things up. In React, markup is a part of your code, so you can use tools like variables and functions to tidy up complex expressions. 对于简单的条件判断,这样的风格可以很好地实现,但需要适量使用。如果你的组件里有很多的嵌套式条件表达式,则需要考虑通过提取为子组件来简化这些嵌套表达式。在 React 里,标签也是你代码中的一部分,所以你可以使用变量和函数来整理一些复杂的表达式。

与运算符(&&)| Logical AND operator (&&)

Another common shortcut you’ll encounter is the JavaScript logical AND (&&) operator. Inside React components, it often comes up when you want to render some JSX when the condition is true, or render nothing otherwise. With &&, you could conditionally render the checkmark only if isPacked is true: 你会遇到的另一个常见的快捷表达式是 JavaScript 逻辑与(&&)运算符。在 React 组件里,通常用在当条件成立时,你想渲染一些 JSX,或者不做任何渲染。使用 &&,你也可以实现仅当 isPackedtrue 时,渲染勾选符号。

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

You can read this as “if isPacked, then (&&) render the checkmark, otherwise, render nothing”. 你可以认为,“当 isPacked 为真值时,则(&&)渲染勾选符号,否则,不渲染。”

Here it is in action: 下面为具体的例子:

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

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride 的行李清单</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="宇航服" 
        />
        <Item 
          isPacked={true} 
          name="带金箔的头盔" 
        />
        <Item 
          isPacked={false} 
          name="Tam 的照片" 
        />
      </ul>
    </section>
  );
}

A JavaScript && expression returns the value of its right side (in our case, the checkmark) if the left side (our condition) is true. But if the condition is false, the whole expression becomes false. React considers false as a “hole” in the JSX tree, just like null or undefined, and doesn’t render anything in its place. 当 JavaScript && 表达式 的左侧(我们的条件)为 true 时,它则返回其右侧的值(在我们的例子里是勾选符号)。但条件的结果是 false,则整个表达式会变成 false。在 JSX 里,React 会将 false 视为一个“空值”,就像 null 或者 undefined,这样 React 就不会在这里进行任何渲染。

陷阱

Don’t put numbers on the left side of &&. 切勿将数字放在 && 左侧.

To test the condition, JavaScript converts the left side to a boolean automatically. However, if the left side is 0, then the whole expression gets that value (0), and React will happily render 0 rather than nothing. JavaScript 会自动将左侧的值转换成布尔类型以判断条件成立与否。然而,如果左侧是 0,整个表达式将变成左侧的值(0),React 此时则会渲染 0 而不是不进行渲染。

For example, a common mistake is to write code like messageCount && <p>New messages</p>. It’s easy to assume that it renders nothing when messageCount is 0, but it really renders the 0 itself! 例如,一个常见的错误是 messageCount && <p>New messages</p>。其原本是想当 messageCount 为 0 的时候不进行渲染,但实际上却渲染了 0

To fix it, make the left side a boolean: messageCount > 0 && <p>New messages</p>. 为了更正,可以将左侧的值改成布尔类型:messageCount > 0 && <p>New messages</p>

选择性地将 JSX 赋值给变量 | Conditionally assigning JSX to a variable

When the shortcuts get in the way of writing plain code, try using an if statement and a variable. You can reassign variables defined with let, so start by providing the default content you want to display, the name: 当这些快捷方式妨碍写普通代码时,可以考虑使用 if 语句和变量。因为你可以使用 let 进行重复赋值,所以一开始你可以将你想展示的(这里指的是物品的名字)作为默认值赋予给该变量。

let itemContent = name;

Use an if statement to reassign a JSX expression to itemContent if isPacked is true: 结合 if 语句,当 isPackedtrue 时,将 JSX 表达式的值重新赋值给 itemContent

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

Curly braces open the “window into JavaScript”. Embed the variable with curly braces in the returned JSX tree, nesting the previously calculated expression inside of JSX: 在 JSX 中通过大括号使用 JavaScript。将变量用大括号嵌入在返回的 JSX 树中,来嵌套计算好的表达式与 JSX:

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

This style is the most verbose, but it’s also the most flexible. Here it is in action: 这种方式是最冗长的,但也是最灵活的。下面是相关的例子:

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 的行李清单</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="宇航服" 
        />
        <Item 
          isPacked={true} 
          name="带金箔的头盔" 
        />
        <Item 
          isPacked={false} 
          name="Tam 的照片" 
        />
      </ul>
    </section>
  );
}

Like before, this works not only for text, but for arbitrary JSX too: 跟之前的一样,这个方式不仅仅适用于文本,任意的 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 的行李清单</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="宇航服" 
        />
        <Item 
          isPacked={true} 
          name="带金箔的头盔" 
        />
        <Item 
          isPacked={false} 
          name="Tam 的照片" 
        />
      </ul>
    </section>
  );
}

If you’re not familiar with JavaScript, this variety of styles might seem overwhelming at first. However, learning them will help you read and write any JavaScript code — and not just React components! Pick the one you prefer for a start, and then consult this reference again if you forget how the other ones work. 如果对 JavaScript 不熟悉,这些不同的风格一开始可能会让你感到不知所措。但是,学习这些将有助于你理解和写任何的 JavaScript 代码,而不仅仅是 React 组件。一开始可以选择一个你喜欢的来用,然后当你忘记其他的怎么用时,可以再翻阅这份参考资料。

摘要

  • In React, you control branching logic with JavaScript.
  • 在 React,你可以使用 JavaScript 来控制分支逻辑。
  • You can return a JSX expression conditionally with an if statement.
  • 你可以使用 if 语句来选择性地返回 JSX 表达式。
  • You can conditionally save some JSX to a variable and then include it inside other JSX by using the curly braces.
  • 你可以选择性地将一些 JSX 赋值给变量,然后用大括号将其嵌入到其他 JSX 中。
  • In JSX, {cond ? <A /> : <B />} means “if cond, render <A />, otherwise <B />.
  • 在 JSX 中,{cond ? <A /> : <B />} 表示 “当 cond 为真值时, 渲染 <A />,否则 <B />
  • In JSX, {cond && <A />} means “if cond, render <A />, otherwise nothing”.
  • 在 JSX 中,{cond && <A />} 表示 “当 cond 为真值时, 渲染 <A />,否则不进行渲染”
  • The shortcuts are common, but you don’t have to use them if you prefer plain if.
  • 快捷的表达式很常见,但如果你更倾向于使用 if,你也可以不使用它们。

1挑战 3 个挑战:
? : 给未完成的物品加上图标 | Show an icon for incomplete items with ? :

Use the conditional operator (cond ? a : b) to render a ❌ if isPacked isn’t true. 当 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 的行李清单</h1>
      <ul>
        <Item 
          isPacked={true} 
          name="宇航服" 
        />
        <Item 
          isPacked={true} 
          name="带金箔的头盔" 
        />
        <Item 
          isPacked={false} 
          name="Tam 的照片" 
        />
      </ul>
    </section>
  );
}