初始代码(点击展开)
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>
) : (
<p>We're still working on our menu. Please come back later</p>
)}
{/* <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);
if (props.pizzaObj.soldOut) {
return null;
}
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 ? (
<Order closeHour={closeHour} />
) : (
<p>
We're happy to welcome you between {openHour}:00 and {closeHour}
</p>
)}
</footer>
);
}
function Order(props) {
return (
<div className="order">
<p>
We're open until {props.closeHour}:00. Come visit us or order online.
</p>
<button className="btn">Order</button>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
什么是 React Fragment 以及为什么需要它?
在 React 开发中,我们经常会遇到需要返回多个并列的 JSX 元素而不想添加额外包装元素的情况。这正是 React Fragment 发挥作用的时候。
React 的 JSX 规则要求每个组件只能返回一个根元素。通常,我们会用一个 <div> 来包裹多个元素,但这可能会导致 DOM 树中出现不必要的嵌套层级,影响样式和语义。
问题示例
让我们看看比萨店菜单应用程序中的一个场景。在 Menu 组件中,我们想要在有比萨的情况下显示一段介绍文字和比萨列表,但在没有比萨的情况下只显示一条提示信息:
function Menu() {
const pizzas = pizzaData;
const numPizzas = pizzas.length;
return (
<main className="menu">
<h2>Our Menu</h2>
{numPizzas > 0 ? (
// 这里我们想要返回段落和列表,但不能直接并列
<p>我们提供多种美味的比萨选择,全部使用新鲜原料制作。</p>
<ul className="pizzas">
{pizzas.map((pizza) => (
<Pizza pizzaObj={pizza} key={pizza.name} />
))}
</ul>
) : (
<p>We're still working on our menu. Please come back later</p>
)}
</main>
);
}但是,上面的代码会报错:”JSX expressions must have one parent element”(JSX 表达式必须有一个父元素)。
解决方案:React Fragment
我们可以使用 React Fragment 来解决这个问题。Fragment 允许我们将多个元素组合在一起,而不会在 DOM 中留下任何痕迹。
简写语法
最常用的 Fragment 语法非常简洁:
{numPizzas > 0 ? (
<>
<p>我们提供多种美味的比萨选择,全部使用新鲜原料制作。</p>
<ul className="pizzas">
{pizzas.map((pizza) => (
<Pizza pizzaObj={pizza} key={pizza.name} />
))}
</ul>
</>
) : (
<p>We're still working on our menu. Please come back later</p>
)}这里的 <>…</> 就是 Fragment 的简写形式。它不会在 DOM 中创建任何额外的元素,只是将其中的内容组合在一起。

完整语法
如果需要为 Fragment 添加属性(如 key),则需要使用完整语法:
{numPizzas > 0 ? (
<React.Fragment key="menu-items">
<p>我们提供多种美味的比萨选择,全部使用新鲜原料制作。</p>
<ul className="pizzas">
{pizzas.map((pizza) => (
<Pizza pizzaObj={pizza} key={pizza.name} />
))}
</ul>
</React.Fragment>
) : (
<p>We're still working on our menu. Please come back later</p>
)}这需要确保我们已经导入了 React:import React from “react”;。
何时使用 Fragment?
Fragment 在以下情况特别有用:
- 当你需要返回多个并列元素时
- 在列表渲染中需要包装多个元素但不想添加额外的 DOM 节点
- 在条件渲染中需要返回多个元素
检查 DOM 结构
使用 Fragment 的一个好处是保持 DOM 结构的清洁。如果我们检查页面的 DOM 树,会发现 Fragment 内的元素直接出现在父元素中,而没有额外的包装层:
<main class="menu">
<h2>Our Menu</h2>
<!-- 下面的元素直接作为 main 的子元素,中间没有额外的 div -->
<p>我们提供多种美味的比萨选择,全部使用新鲜原料制作。</p>
<ul class="pizzas">
<!-- 列表项内容 -->
</ul>
</main>更广泛的应用
Fragment 不仅可以用于条件渲染,还可以用于组件的根元素。例如,我们可以修改 App 组件,让它直接返回三个组件,而不是将它们包裹在一个容器中:
function App() {
return (
<>
<Header />
<Menu />
<Footer />
</>
);
}这样,这三个组件会直接成为渲染目标的子元素,没有额外的包装层。
结论
React Fragment 是一个简单但非常有用的功能,它解决了 JSX 只能有一个根元素的限制,同时保持了 DOM 结构的清洁。通过使用 Fragment,我们可以创建更灵活、更语义化的组件结构,不必担心添加不必要的 DOM 元素。
在实际开发中,Fragment 已成为 React 开发者工具箱中不可或缺的工具。无论是条件渲染、列表渲染还是组件结构优化,Fragment 都能帮助我们写出更优雅的代码。
完整代码(点击展开)
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 ? (
<>
<p>我们提供多种美味的比萨选择,全部使用新鲜原料制作。</p>
<ul className="pizzas">
{pizzas.map((pizza) => (
<Pizza pizzaObj={pizza} key={pizza.name} />
))}
</ul>
</>
) : (
<p>We're still working on our menu. Please come back later</p>
)}
{/* <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);
if (props.pizzaObj.soldOut) {
return null;
}
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 ? (
<Order closeHour={closeHour} />
) : (
<p>
We're happy to welcome you between {openHour}:00 and {closeHour}
</p>
)}
</footer>
);
}
function Order(props) {
return (
<div className="order">
<p>
We're open until {props.closeHour}:00. Come visit us or order online.
</p>
<button className="btn">Order</button>
</div>
);
}
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);


发表回复