React 条件包装模式实战指南
讲解在 React 中优雅实现条件包装,提高代码可读性和可维护性。React 开发者可直接应用。
讲解在 React 中优雅实现条件包装,提高代码可读性和可维护性。React 开发者可直接应用。
这并不是一个经常会用到的技巧,但我为那些正在寻找这种实现方式的人写了这篇文章。
有时,我们可能会有一个通用元素,比如某个渲染在 modal 内的特定组件。当某个 flag 被设置时,这个组件需要增加一层父级 wrapper,以不同的变体形式展示。
我们当然可以使用 if…else 语句,但这样写看起来很凌乱。
假设我们有一些特定的服务卡片,以此为例会更容易理解。在某些情况下,它们只是介绍一项服务;但在另一些情况下,它们需要链接到详情页面。
这个组件可能如下所示:
const ServiceCard = ({ title, description, image, url }) => {
return (
<section>
<h2>{title}</h2>
<p>{description}</p>
<img src={image} alt={title} />
</section>
);
};
正如前面提到的,如果 URL 存在,我们需要用一个 link 元素包裹整个组件,该怎么办?我们可以使用 if…else 结构:
const ServiceCard = ({ title, description, image, url }) => {
return (
<section>
{url ? (
<a href={url}>
<h2>{title}</h2>
<p>{description}</p>
<img src={image} alt={title} />
</a>
) : (
<>
<h2>{title}</h2>
<p>{description}</p>
<img src={image} alt={title} />
</>
)}
</section>
);
};
但这样会产生重复代码,显得有些笨拙。如果需要调整每个元素的样式,我们就必须在两个地方分别修改。
那么,怎样才能更优雅地实现条件包裹呢?
我们可以创建一个通用组件来处理这件事。这个组件名为 ConditionalWrapper,它会接收 condition、wrapper,以及需要包裹的 children。
const ConditionalWrapper = ({ condition, wrapper, children }) =>
condition ? wrapper(children) : children;
有了它,我们就可以在现有组件中使用它,让代码变得更简洁。
const ServiceCard = ({ title, description, image, url }) => {
return (
<section>
<ConditionalWrapper
condition={url}
wrapper={(children) => <a href={url}>{children}</a>}
>
<>
<h2>{title}</h2>
<p>{description}</p>
<img src={image} alt={title} />
</>
</ConditionalWrapper>
</section>
);
};
现在使用这个组件时,它会根据是否传入 URL,决定是否渲染带有 href 的链接。最棒的是,我们的元素不再有任何重复代码。
例如,以下用法:
<ServiceCard title='test' description='foo bar' img='img1.jpg' />
会返回下面的 HTML:
<section>
<h2>test</h2>
<p>foo bar</p>
<img src="img1.jpg" alt="test" />
</section>
如果在元素中传入 URL,则会得到下面的输出:
<section>
<a href="url">
<h2>test</h2>
<p>foo bar</p>
<img src="img1.jpg" alt="test" />
</a>
</section>
当然,真正的关键在于 ConditionalWrapper 组件,更准确地说,在于 wrapper 参数。
由于我们传入了 children(这是 React 的默认 prop),因此可以看到,wrapper={children => <a href={url}>{children}</a>} 这种函数用法表达的是:
如果满足 condition:
将 children 包裹在这个特定元素中。
你可能只有少数几次会需要这个函数,但在那些场景里,它能帮上大忙。
注意:特别感谢 Olivier 提供的原始思路!
感谢阅读我的博客。欢迎订阅我的邮件 Newsletter,也可以通过 Facebook 或 Twitter 与我联系。