原文连接:www.robinwieruch.de/conditional…
原做者:Robin Wieruchreact
在 React 中使用条件渲染并不困难。在 JSX 中——用于 React 的语法扩展——您可使用纯 JavaScript,其中包括 if else 语句、三元操做符、 switch case 语句等等。在条件渲染中,React 组件根据一个或多个条件决定将返回哪些 DOM 元素。 例如,基于某种逻辑,它能够返回一个项目列表或者一个文本,上面写着“对不起,列表是空的”。当组件具备条件渲染时,渲染的组件外观根据条件不一样而不一样。本文旨在为 React 中的条件渲染和这些模式的最佳实践提供一个详尽的选择清单。函数
React 中最基本的条件渲染逻辑是用一个 if 语句完成的。假设您不想在 React 组件中渲染某些内容,由于它没有必要的 React props 可用。 例如,若是开始没有list的话,React 中的 List 组件不该该在视图中渲染list的HTML元素。 你可使用一个简单的 JavaScript if 语句提早返回(守卫模式) :学习
const users = [
{ id: '1', firstName: 'Robin', lastName: 'Wieruch' },
{ id: '2', firstName: 'Dennis', lastName: 'Wieruch' },
];
function App() {
return (
<div>
<h1>Hello Conditional Rendering</h1>
<List list={users} />
</div>
);
}
function List({ list }) {
if (!list) {
return null;
}
return (
<ul>
{list.map(item => (
<Item key={item.id} item={item} />
))}
</ul>
);
}
function Item({ item }) {
return (
<li>
{item.firstName} {item.lastName}
</li>
);
}
复制代码
您能够本身尝试将用户设置为 null 或 undefined。 若是props中的信息为 null 或 undefined,则 React 组件在条件渲染中返回 null。 在这里,返回 null 而不是 JSX 的 React 组件将不渲染任何内容。ui
在这个例子中,咱们已经完成了基于 props 的条件渲染,可是条件渲染也能够基于 state 和 hooks 。 注意,咱们尚未在 JSX 内部使用 if 语句,而只是在 return 语句以前在外部使用 if 语句。spa
让咱们继续前面的例子来学习 React 中的 if else 语句。 若是没有list,咱们就不渲染任何内容,并隐藏 HTML,就像咱们以前用单个 if 语句所看到的那样。 可是,为了得到更好的用户体验,当list为空时,您可能但愿为用户显示一个文本做为反馈。 这将与另外一个 if 语句一块儿使用,可是咱们将用 if else 语句扩展这个例子:code
function List({ list }) {
if (!list) {
return null;
}
if (!list.length) {
return <p>Sorry, the list is empty.</p>;
} else {
return (
<div> {list.map(item => ( <Item item={item} /> ))} </div> ); } } 复制代码
如今,List 组件根据一些 JavaScript 逻辑不渲染任何内容、文本或列表。尽管前面的例子展现了如何在 React 中使用 if else 语句,我仍是建议在每次须要保护主返回内容时使用单个 if 语句(这里: 返回列表)做为最佳实践:xml
function List({ list }) {
if (!list) {
return null;
}
if (!list.length) {
return <p>Sorry, the list is empty.</p>;
}
return (
<div> {list.map(item => ( <Item item={item} /> ))} </div> ); } 复制代码
这比以前的 if else 条件渲染更具可读性。 全部守卫整齐地排列为主返回语句以前的 if 语句,这也能够解释为隐含的 else 语句。 尽管如此,在 return 语句中尚未使用 if 和 else 语句。对象
的确,咱们能够在 JSX 中使用 JavaScript,可是在 JSX 中使用 if,else,switch case 这样的语句会变得很困难。 没有真正的方法来内联它。 在 JavaScript 中表达 if else 语句的另外一种方式是三元运算符:教程
// if else
function getFood(isVegetarian) {
if (isVegetarian) {
return 'tofu';
} else {
return 'fish';
}
}
// ternary operator
function getFood(isVegetarian) {
return isVegetarian ? 'tofu' : 'fish';
}
复制代码
例如,假设您的组件显示预览或编辑模式。 条件是一个 JavaScript 布尔值,以 React prop 的形式出现。 你可使用布尔值来决定你想要条件渲染哪一个元素:接口
function Recipe({ food, isEdit }) {
return (
<div>
{food.name}
{isEdit ? (
<EditRecipe food={food} />
) : (
<ShowRecipe food={food} />
)}
</div>
);
}
复制代码
三元操做符中两个隐式 return 语句周围的括号()使您可以返回单个或多个 HTML 元素,或从那里返回 React 组件。 若是只有一个元素,能够省略括号。
注意: 有时候你须要用一个 div 元素包裹多行元素做为一个块。 无论怎样,尽可能保持轻量化。 若是()之间包裹的内容太大,能够考虑将其做为组件提取,如示例中所示。
在 React 中使用三元运算渲染不只使条件渲染更加简洁,并且为你在返回中内嵌条件渲染提供了一种简单的方法。 这样,只有一部分 JSX 是有条件地渲染的,而其余部分能够不受任何条件限制地保持完整。
这种状况常常发生,你想要渲染一个元素或者什么都不渲染。 你已经知道一个简单的if条件能够帮助解决这个问题。 可是,您仍是但愿可以像三元运算符那样内联条件。 使用下面的加载指示符组件,它使用条件三元操做符返回元素或者什么都不返回:
function LoadingIndicator({ isLoading }) {
return <div>{isLoading ? <p>Loading...</p> : null}</div>;
}
复制代码
这个完成的不错,你在你的 JSX 内联了条件。 然而,还有一种替代方法能够省略返回 null 的必要性。
逻辑 && 运算符能够帮助您使返回 null 的条件更简洁。 在 JavaScript 中,一个 true && Hello World 老是等于 Hello World。 False && Hello World 老是被计算为 false:
const result = true && 'Hello World';
console.log(result);
// Hello World
const result = false && 'Hello World';
console.log(result);
// false
复制代码
在React中,你能够利用这种行为。 若是条件为真,逻辑 && 运算符后面的表达式将做为输出。 若是条件为 false,React 忽略并跳过表达式:
function LoadingIndicator({ isLoading }) {
return <div>{isLoading && <p>Loading...</p>}</div>;
}
复制代码
当您但愿不返回任何内容或 JSX 中的元素时,这是您的方式。 它也被称为短路求值,这使得它甚至比三元操做符更简洁。
如今可能会出现多个条件渲染的状况。 例如,通知组件基于状态字符串渲染错误、警告或信息组件:
function Notification({ text, status }) {
if (status === 'info') {
return <Info text={text} />;
}
if (status === 'warning') {
return <Warning text={text} />;
}
if (status === 'error') {
return <Error text={text} />;
}
return null;
}
复制代码
您可使用 switch case 操做符来处理多个条件渲染:
function Notification({ text, status }) {
switch (status) {
case 'info':
return <Info text={text} />;
case 'warning':
return <Warning text={text} />;
case 'error':
return <Error text={text} />;
default:
return null;
}
}
复制代码
对 switch case 操做符使用 default 是明智的,由于 React 组件老是必须返回一个元素或 null。 若是一个组件有一个基于字符串的条件渲染,那么用 TypeScript 来描述组件的接口是有意义的:
type Status = 'info' | 'warning' | 'error';
type NotificationProps = {
text: string;
status: Status;
};
function Notification({ text, status }: NotificationProps) {
switch (status) {
case 'info':
return <Info text={text} />;
case 'warning':
return <Warning text={text} />;
case 'error':
return <Error text={text} />;
default:
return null;
}
}
复制代码
对于多个条件渲染来讲,switch case 写是一个很好的开始。 可是它也有一样的缺点,就像 if else 语句同样。 不能在 JSX 中使用 switch case,对吧? 实际上它能够经过一个条件渲染函数来实现,这个函数是自调用的:
function Notification({ text, status }) {
return (
<div>
{(function() {
switch (status) {
case 'info':
return <Info text={text} />;
case 'warning':
return <Warning text={text} />;
case 'error':
return <Error text={text} />;
default:
return null;
}
})()}
</div>
);
}
复制代码
能够选择使用条件渲染箭头函数使 switch case 更简洁:
function Notification({ text, status }) {
return (
<div>
{(() => {
switch (status) {
case 'info':
return <Info text={text} />;
case 'warning':
return <Warning text={text} />;
case 'error':
return <Error text={text} />;
default:
return null;
}
})()}
</div>
);
}
复制代码
总之,switch case 操做符能够帮助您实现多个条件渲染。 可是这是最好的方法吗? 让咱们看看如何用枚举来代替多个条件渲染。
带有映射关系的键值对的 JavaScript 对象称为枚举:
const NOTIFICATION_STATES = {
info: 'Did you know? ...',
warning: 'Be careful here ...',
error: 'Something went wrong ...',
};
复制代码
枚举是处理 React 中带有多个条件的条件渲染的一种很好的方法。 它们比 switch case 语句更给力,由于它们能够在 JSX 中使用。 让咱们再次考虑通知组件,但此次使用枚举做为内联对象(内部花括号) :
function Notification({ text, status }) {
return (
<div>
{
{
info: <Info text={text} />,
warning: <Warning text={text} />,
error: <Error text={text} />,
}[status]
}
</div>
);
}
复制代码
status 属性键帮助咱们从对象中检索值。 很棒,不是吗? 与 switch case 写操做符相比,它更具可读性。
在本例中,咱们必须使用内联对象,由于对象的值依赖于 text 属性。 不管如何,这是我推荐的方式。 可是,若是它不依赖于 text 属性,你可使用一个枚举做为条件渲染的常量:
const NOTIFICATION_STATES = {
info: <Info />, warning: <Warning />, error: <Error />, }; function Notification({ status }) { return ( <div> {NOTIFICATION_STATES[status]} </div> ); } 复制代码
这样事情就解决了。 若是咱们仍然依赖以前的 text 属性,咱们可使用一个带有函数的条件渲染来检索值:
const getNotification = text => ({
info: <Info text={text} />,
warning: <Warning text={text} />,
error: <Error text={text} />,
});
function Notification({ status, text }) {
return <div>{getNotification(text)[status]}</div>;
}
复制代码
毕竟,React 中的枚举条件渲染比 switch case 语句更优雅。 做为枚举的对象提供了大量的选项来实现多个条件渲染。 布尔型的排列也是可能的:
function Message({ isExtrovert, isVegetarian }) {
const key = `${isExtrovert}-${isVegetarian}`;
return (
<div> { { 'true-true': <p>I am an extroverted vegetarian.</p>, 'true-false': <p>I am an extroverted meat eater.</p>, 'false-true': <p>I am an introverted vegetarian.</p>, 'false-false': <p>I am an introverted meat eater.</p>, }[key] } </div>
);
}
复制代码
最后一个例子有点过头了,我不建议使用它。 然而,当涉及到条件渲染,枚举是我最喜欢的一个React模式。
那么在 React 中嵌套条件渲染呢? 是的,这是可能的。 例如,让咱们看看以前的 List 组件,它显示了一个列表、一个空文本或者什么都没有:
function List({ list }) {
const isNotAvailable = !list;
const isEmpty = !list.length;
return (
<div> {isNotAvailable ? <p>Sorry, the list is not there.</p> : (isEmpty ? <p>Sorry, the list is empty.</p> : <div>{list.map(item => <Item item={item} />)}</div> ) } </div> ); } 复制代码
它能够工做,但我建议避免嵌套条件渲染,由于他们是冗长的,这使得它不太可读。 试试下面的解决方案:
高阶组件(Higher-Order Components,HOCs)是 React 中条件渲染的完美匹配。 Hoc 能够帮助处理多个使用场景,可是一个场景多是使用条件渲染来改变组件的外观。 让咱们看看一个显示元素或组件的 HOC:
// Higher-Order Component
function withLoadingIndicator(Component) {
return function EnhancedComponent({ isLoading, ...props }) {
if (!isLoading) {
return <Component {...props} />;
}
return (
<div>
<p>Loading</p>
</div>
);
};
}
const ListWithLoadingIndicator = withLoadingIndicator(List);
function App({ list, isLoading }) {
return (
<div>
<h1>Hello Conditional Rendering</h1>
<ListWithLoadingIndicator isLoading={isLoading} list={list} />
</div>
);
}
复制代码
在这个示例中,List 组件能够专一于渲染列表。 它没必要担忧加载状态。 一个 HOC 在您的实际组件中隐藏了全部的噪音。 最终,您能够添加多个高阶组件(组合) ,以隐藏多个条件渲染边缘状况。 做为 hoc 的替代品,你也可使用 render prop 支持条件渲染。
最后但并不是最不重要的是,有外部库处理标记级别上的条件渲染。 他们添加了控制组件,以便在没有 JS 的状况下启用条件渲染:
<Choose>
<When condition={isLoading}>
<div><p>Loading...</p></div>
</When>
<Otherwise>
<div>{list.map(item => <Item item={item} />)}</div>
</Otherwise>
</Choose>
复制代码
不少人认为React(包括JSX)是他们的首选库,由于他们能够在JSX中使用纯HTML和JS处理渲染。
我但愿这个 React 教程对您学习条件渲染有所帮助。 若是你喜欢它,请与你的朋友分享。 最后,我为你准备了一个所有条件渲染的备忘单: