创建可重用组件的技巧
原文发布于:dylanpaulus.com
介绍
React 的一大卖点在于其可组合、可复用的组件。所有组件都基于 的理念构建V = F(d)
,或者说视图/UI 是由一些作用于数据/状态的函数创建的。那么,我们如何创建更高级的用户界面呢?只需添加更多函数即可(例如)。这很巧妙,但为什么这很重要呢?即使我们使用类V = G(F(E(S(d))))
,将组件视为函数也能帮助我们创建更多可复用的组件。它甚至可以帮助我们编写非常有用的实用函数,即高阶组件。
我们将通过一个示例组件来探讨如何提高组件的可重用性和组合性。在本文中,我们将使用一个向按钮添加图标的组件。
例如,
class IconButton extends React.Component {
render() {
<Button onClick={this.props.onClick}>
<Icon />
{" "}
{this.props.text}
</Button>
}
}
并消耗按钮,
<IconButton
text="Click Me!"
onClick={() => alert("Click!")}
/>
还不错……简单易用,而且切中要点。不过,我还是觉得还可以改进。让我们来看看一些关于如何提高这个组件复用性的建议。
保持简单,杰克
继续讨论将组件视为函数的话题,保持组件简洁。功能过多的组件很危险。它们很容易损坏,维护起来也很麻烦。相反,应该将功能拆分成独立的组件,然后组合这些组件来实现功能。这样可以非常轻松地维护功能,并在需求发生变化时轻松替换功能。
回到 IconButton 的例子,我们实际上并不需要 IconButton 组件。如果明天我们需要一个 IconInput 按钮怎么办?我们不仅要维护两个独立且非常相似的组件!我们可以通过使用组合来解决这个问题。我们真正想要的是能够“为组件添加图标”的功能。让我们创建一个名为 IconAdder 的新组件。
class IconAdder extends React.Component {
render() {
return (
<div>
<Icon />
{" "}
{this.props.component}
</div>
);
}
}
消耗它
<IconAdder
component={
<Button onClick={() => alert("Click!")}>
Click Me!
</Button>
}
/>
IconAdder 允许我们将任何旧组件放入其中,然后它会为其添加图标。太棒了。
哈喽,孩子们!
我经常遇到的一个问题是组件属性的过度使用。组件几乎不应该被用作属性。这最终会使组件难以使用,最终导致我们与 React 产生冲突。在 React 中,每个组件都有一个名为 children ( ) 的属性。这意味着,无论我们在这个组件的标签之间传递什么,都会插入到这里。让我们看一个IconAdder 组件中的props.children
例子。props.children
class IconAdder extends React.Component {
render() {
return (
<div>
<Icon />
{" "}
{this.props.children}
</div>
);
}
}
消耗它
<IconAdder>
<Button onClick={() => alert("Click!")}>
Click Me!
</Button>
</IconAdder>
该<Button>
组件将插入 IconAdder 中{this.props.children}
!好多了!
注入 HTML 类型
我已经在这里写了一篇关于这个主题的文章,所以我会简短地说一下。
作为组件的使用者,我希望代码污染尽可能少。在这种情况下,污染可以是任何东西:事件处理程序、代码大小,甚至是 HTML 元素。就我们的 IconAdder 组件而言,<div>
每个使用它的组件都会添加一个父包装器。如果我们能去掉 div,直接把它作为子组件就好了。好吧……我们很幸运。我们可以指定一个 tag 属性,然后使用该属性。这让最终用户可以控制他们的 DOM 结构。
class IconAdder extends React.Component {
render() {
const { tag, onClick, children } = this.props;
const Tag = tag;
return (
<Tag onClick={onClick}>
<Icon />
{" "}
{children}
</Tag>
);
}
}
消耗它
<IconAdder
tag={Button}
onClick={() => alert("Click!")}
>
Click Me!
</IconAdder>
使用 'tag' 属性的另一个好处是,假设我们想要支持 React Router 的<Link>
组件,但也需要支持经典的 HTML<a>
标签。我们只需更改 'tag' 属性即可轻松切换两者!
允许扩展
没有什么比拿到一个全新的组件,却突然意识到要把字体加粗更让我抓狂的了。于是,我style={ { fontWeight: 800 } }
给组件添加了一个快捷键,刷新了一下,结果什么也没变。(警告:即将显示的内容需要转译器——Babel插件)。
我们应该允许组件合理地进行编辑,同时保护我们需要保护的内容。这可以通过展开运算符来实现。
目前,我们的 IconAdder 组件还不支持 style、className 或 title 属性。对了,如果以后需要在<a>
标签上使用 IconAdder 怎么办?我们还需要支持 href。让我们使用 rest 运算符来支持所有这些属性!
class IconAdder extends React.Component {
render() {
const { tag, onClick, children, ...rest } = this.props;
const Tag = tag;
return (
<Tag onClick={onClick} {...rest}>
<Icon />
{" "}
{children}
</Tag>
);
}
}
消耗它
<IconAdder
tag={Button}
onClick={() => alert("Click!")}
style={ { fontWeight: "800" } }
title="A button for clicking"
>
Click Me!
</IconAdder>
...rest
它会抓取我们在解构()中未移除的所有属性const { a, b, c} = this.props;
,并将其赋值给一个名为 的变量。然后,我们将对象解构为各个 props,并将rest
所有这些属性应用到组件中。正如 IconAdder 的使用示例所示,我们现在可以添加任何我们想要的其他属性了!<Tag>
rest
结论
以下是我在创建组件时使用的一些技巧,旨在使它们更易于使用和扩展。总而言之,将每个组件视为一个函数。它会接收一些输入,并产生一个输出。children 属性是你的好朋友,在合理的情况下使用它!动态注入组件类型可以将多个相似的组件转换为一个高度可复用的组件。最后,允许组件接收额外的 props,并在可能的情况下让属性/样式覆盖基础实现。
文章来源:https://dev.to/ganderzz/tips-on-creating-reusable-components-376j