17. React条件渲染深度解析:逻辑与(&&)运算符的使用技巧

在React开发中,条件渲染是我们经常使用的一项重要技术。它允许我们根据特定条件决定是否渲染某段JSX代码或整个组件。今天,紧接着上一小节的代码,我将详细探讨如何使用”&”运算符实现条件渲染,并分享一些使用过程中的注意事项。

初始代码(点击显示)
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";

const pizzaData = [
  {
    name: "Focaccia",
    ingredients: "Bread with italian olive oil and rosemary",
    price: 6,
    photoName: "pizzas/focaccia.jpg",
    soldOut: false,
  },
  {
    name: "Pizza Margherita",
    ingredients: "Tomato and mozarella",
    price: 10,
    photoName: "pizzas/margherita.jpg",
    soldOut: false,
  },
  {
    name: "Pizza Spinaci",
    ingredients: "Tomato, mozarella, spinach, and ricotta cheese",
    price: 12,
    photoName: "pizzas/spinaci.jpg",
    soldOut: false,
  },
  {
    name: "Pizza Funghi",
    ingredients: "Tomato, mozarella, mushrooms, and onion",
    price: 12,
    photoName: "pizzas/funghi.jpg",
    soldOut: false,
  },
  {
    name: "Pizza Salamino",
    ingredients: "Tomato, mozarella, and pepperoni",
    price: 15,
    photoName: "pizzas/salamino.jpg",
    soldOut: true,
  },
  {
    name: "Pizza Prosciutto",
    ingredients: "Tomato, mozarella, ham, aragula, and burrata cheese",
    price: 18,
    photoName: "pizzas/prosciutto.jpg",
    soldOut: false,
  },
];

function App() {
  return (
    <div className="container">
      <Header />
      <Menu />
      <Footer />
    </div>
  );
}

function Header() {
  const style = {};

  return (
    <header className="header">
      <h1 style={style}>Fast React Pizza Co.</h1>
    </header>
  );
}

function Menu() {
  return (
    <main className="menu">
      <h2>Our Menu</h2>
      <ul className="pizzas">
        {pizzaData.map((pizza) => (
          <Pizza pizzaObj={pizza} key={pizza.name}/>
        ))}
      </ul>
      {/* <Pizza
        name="Pizza Spinaci"
        ingredient="Tomato, mozarella, spinach, and ricotta cheese"
        photoName="pizzas/spinaci.jpg"
        price={10}
      />
      <Pizza
        name="Pizza Funghi"
        ingredient="Tomato, mushrooms"
        price={12}
        photoName="pizzas/funghi.jpg"
      /> */}
    </main>
  );
}

function Pizza(props) {
  console.log(props);
  return (
    <li className="pizza">
      <img src={props.pizzaObj.photoName} alt={props.pizzaObj.name}></img>
      <div>
        <h3>{props.pizzaObj.name}</h3>
        <p>{props.pizzaObj.ingredients}</p>
        <span>{props.pizzaObj.price}</span>
      </div>
    </li>
  );
}

function Footer() {
  const hour = new Date().getHours();
  const openHour = 12;
  const closeHour = 22;
  const isOpen = hour >= openHour && hour <= closeHour;

  console.log(isOpen);

  return (
    <footer className="footer">
      {new Date().toLocaleDateString()}. We're currently open.
    </footer>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

什么是条件渲染?

条件渲染简单来说就是基于某个条件决定是否显示UI的某个部分。这个条件可以是任何返回true或false的表达式。在实际应用中,这种技术非常有用,比如:

  • 只在餐厅营业时显示”下单”按钮
  • 只在有数据时显示列表
  • 只对登录用户显示某些功能

使用 && 运算符进行条件渲染

在React中使用 && 运算符进行条件渲染利用了JavaScript的”短路求值”(short-circuiting)特性。

基本原理

当我们使用表达式 condition && expression 时:

  • 如果condition为真(truthy),则返回expression
  • 如果condition为假(falsy),则直接返回condition,不会执行expression

实际示例:

我们修改Footer组件里面如下代码⬇️

{isOpen && <p>We're open until {closeHour}. Come visit us or order online.</p>}

在这个例子中:

  • 当isOpen为true时,段落内容会被渲染
  • 当isOpen为false时,React不会渲染任何内容(因为React不会渲染布尔值)

需要注意的陷阱

使用 &&运算符进行条件渲染时有一个常见陷阱需要注意。让我们通过一个例子来说明:

在menu组件中假设我们想在有披萨数据时才渲染披萨列表:

{pizzas && <ul>{pizzas.map(...)}</ul>}

这段代码看起来没问题,但有一个潜在问题:

  • 当pizzas是空数组时,它仍然是一个truthy值!
  • 因此即使没有披萨数据,<ul></ul>仍然会被渲染

更好的方法是检查数组的长度:

{pizzas.length && <ul>{pizzas.map(...)}</ul>}

但这里又有一个问题:

  • 当pizzas.length为0时,& 运算符会返回0
  • React会将0渲染到UI中

正确的做法应该是使用布尔表达式:

{pizzas.length > 0 && <ul>{pizzas.map(...)}</ul>}

&& 运算符的优缺点

优点

  • 语法简洁
  • 对于简单条件渲染很方便

缺点

  • 容易出现上述陷阱
  • 没有”else”分支处理
  • 如果条件是数字0,可能会意外渲染0

结论

虽然有些开发者认为不应该使用 && 运算符进行条件渲染,但我认为在特定场景下它仍然是一个有用的工具。不过,对于大多数条件渲染场景,三元运算符(条件 ? 表达式1 : 表达式2)可能是更安全、更灵活的选择。

完整代码(点击显示)
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";

const pizzaData = [
  {
    name: "Focaccia",
    ingredients: "Bread with italian olive oil and rosemary",
    price: 6,
    photoName: "pizzas/focaccia.jpg",
    soldOut: false,
  },
  {
    name: "Pizza Margherita",
    ingredients: "Tomato and mozarella",
    price: 10,
    photoName: "pizzas/margherita.jpg",
    soldOut: false,
  },
  {
    name: "Pizza Spinaci",
    ingredients: "Tomato, mozarella, spinach, and ricotta cheese",
    price: 12,
    photoName: "pizzas/spinaci.jpg",
    soldOut: false,
  },
  {
    name: "Pizza Funghi",
    ingredients: "Tomato, mozarella, mushrooms, and onion",
    price: 12,
    photoName: "pizzas/funghi.jpg",
    soldOut: false,
  },
  {
    name: "Pizza Salamino",
    ingredients: "Tomato, mozarella, and pepperoni",
    price: 15,
    photoName: "pizzas/salamino.jpg",
    soldOut: true,
  },
  {
    name: "Pizza Prosciutto",
    ingredients: "Tomato, mozarella, ham, aragula, and burrata cheese",
    price: 18,
    photoName: "pizzas/prosciutto.jpg",
    soldOut: false,
  },
];

function App() {
  return (
    <div className="container">
      <Header />
      <Menu />
      <Footer />
    </div>
  );
}

function Header() {
  const style = {};

  return (
    <header className="header">
      <h1 style={style}>Fast React Pizza Co.</h1>
    </header>
  );
}

function Menu() {
  const pizzas = pizzaData;

  // const pizzas = [];
  const numPizzas = pizzas.length;

  return (
    <main className="menu">
      <h2>Our Menu</h2>

      {numPizzas > 0 && (
        <ul className="pizzas">
        {pizzas.map((pizza) => (
          <Pizza pizzaObj={pizza} key={pizza.name} />
        ))}
      </ul>
      ) }

      
      {/* <Pizza
        name="Pizza Spinaci"
        ingredient="Tomato, mozarella, spinach, and ricotta cheese"
        photoName="pizzas/spinaci.jpg"
        price={10}
      />
      <Pizza
        name="Pizza Funghi"
        ingredient="Tomato, mushrooms"
        price={12}
        photoName="pizzas/funghi.jpg"
      /> */}
    </main>
  );
}

function Pizza(props) {
  console.log(props);
  return (
    <li className="pizza">
      <img src={props.pizzaObj.photoName} alt={props.pizzaObj.name}></img>
      <div>
        <h3>{props.pizzaObj.name}</h3>
        <p>{props.pizzaObj.ingredients}</p>
        <span>{props.pizzaObj.price}</span>
      </div>
    </li>
  );
}

function Footer() {
  const hour = new Date().getHours();
  const openHour = 20;
  const closeHour = 22;
  const isOpen = hour >= openHour && hour <= closeHour;

  console.log(isOpen);

  return (
    <footer className="footer">
      {isOpen && (
        <div className="order">
        <p>We're open until {closeHour}:00. Come visit us or order online.</p>
        <button className="btn">Order</button>
        </div>)}
    </footer>
  );
}

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

Comments

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注