
由Kristofer Selbekk与Caroline Odden合作撰写。 基于2019年6月在奥斯陆举行的ReactJS会议上同名同人的演讲。
从翻译者那里看, 《十个组件诫命》的原始名称没有提到React,但是大多数示例和建议都专门涉及到React,此外,本文被放置在react标签下,并且开发人员编写了react 。
创建许多开发人员将要使用的组件并不容易。 如果这些道具是公共API的一部分,则必须仔细考虑要使用哪些道具。
在本文中,我们将简要介绍一些用于整体开发API的最佳实践,并形成十个可用于创建开发人员乐于使用的组件的命令。

什么是API?
API(或应用程序编程接口)是两个代码相遇的地方。 这是您的代码与世界其他地方之间的接触面。 我们称此表面为界面。 这是可以与之交互的一组特定的操作或数据点。
该类与调用该类的代码之间的接口也是API。 您可以调用该类的方法来接收数据或运行其中包含的函数。
遵循相同的原则, 您的组件接受的道具,这就是它的API 。 这是开发人员与您的组件进行交互的方式。
一些最佳的API设计实践
那么,开发API时应遵循哪些规则和注意事项? 好吧,我们进行了一些研究,结果发现,在这个问题上有很多宝贵的资源。 我们选择了两个-Josh Tauberer- “什么才是好的API?” 和罗恩·库里尔 ( Ron Kurir)的同名文章-谈到了这四种做法。
稳定版本
创建API时要考虑的最重要的事情之一就是使其保持尽可能的稳定。 关键更改的数量应最少。 如果必须进行重大更改,请确保编写详细的更新指南,并在可能的情况下,编写对开发人员而言可自动执行此过程的mod代码。
描述性错误消息
每当在调用API时发生错误时,您都应该尽一切可能解释发生了什么问题以及如何解决。 如果您用“滥用”之类的消息责骂用户,并且不给出任何解释,则您的API会留下不好的印象。
而是编写描述性错误,以帮助用户修复他们如何使用您的API。
减少开发人员的意外
开发人员是脆弱的生物,当他们使用您的API时,您不应惊吓它们。 换句话说,使您的API尽可能直观。 如果遵循最佳做法和现有的命名约定,则将实现此目的。
另外,您的代码应始终保持一致。 如果在一个位置使用is
或has
的属性的逻辑名称,但将其进一步跳过,则会使人们is
困惑。
最小化API表面
您的API也需要最小化。 许多功能都很棒,但是API的表面(API表面)越小,开发人员就必须对其进行研究以开始进行高效生产的工作就越少。 因此,您的API将被视为易于使用!
总有一种控制API大小的方法。 其中之一是从旧版本重构新的API。
Web组件的十诫

因此,这四个黄金规则对于REST API和Pascal上的旧程序非常有效-但是如何将它们转移到React的现代世界中?
如前所述,组件具有自己的API。 我们称它们为props
,正是在他们的帮助下,数据才被传输到组件。 我们如何构造道具以免违反上述任何规则?
我们创建了此十个黄金规则的列表,在创建组件时最好遵循这些规则 。 我们希望它们对您有用。
1.记录组件的使用
如果未记录您要使用组件的方式,则此组件无用。 好吧,几乎没有用,您总是可以看一下它的实现,但是很少有人喜欢这样做。
有多种方法来记录组件,但是我们建议您注意以下三种:
前两个为您提供了开发组件时的工作空间,第三个使您可以使用MDX以自由格式编写文档
无论您选择什么,都必须同时记录API本身以及应如何以及何时使用组件的文档。 最后一部分对于通用库至关重要-人们可以在给定的上下文中正确使用按钮或布局网格。
2.启用上下文语义
HTML是一种用于以语义方式构造信息的语言。 仅在这里,我们的大多数组件都由<div />
标记组成。 这是有道理的-通用组件无法预先知道它们将是什么,可能是<article />
或<section />
或<aside />
-但是这种情况远非理想。
还有另一种选择,只是让您的组件接受prop as
,从而确定将呈现哪个DOM元素。 这是如何实现此示例:
function Grid({ as: Element, ...props }) { return <Element className="grid" {...props} /> } Grid.defaultProps = { as: 'div', };
我们将prop重命名as
Element
变量,并在我们的JSX中使用它。 如果没有更多的语义HTML标记可以传递,我们将提供一个通用的默认div
值。
当需要使用<Grid />
组件时,您只需传递正确的标签即可:
function App() { return ( <Grid as="main"> <MoreContent /> </Grid> ); }
这也适用于React组件。 例如,如果您希望<Button />
组件呈现React Router <Link />
:
<Button as={Link} to="/profile"> Go to Profile </Button>
3.避免布尔道具
逻辑道具是个好主意。 它们可以毫无价值地使用,因此看起来非常优雅:
<Button large>BUY NOW!</Button>
但是,尽管看起来不错,但逻辑属性仅允许两种可能性。 开启或关闭 可见或隐藏。 1或0。
每当您开始为大小,选项,颜色或除二进制选择之外的任何其他内容引入逻辑属性时,都会遇到问题。
<Button large small primary disabled secondary> ?? </Button>
换句话说,逻辑属性通常不会随需求的变化而扩展。 取而代之的是,对于可能成为二进制选择之外的值的情况,最好使用枚举值,例如字符串。
<Button variant="primary" size="large"> </Button>
这并不意味着完全不能使用逻辑属性。 可以! 我上面列出的disabled
道具应该仍然合乎逻辑-因为开和关之间没有中间状态。 仅保留布尔属性仅用于真正的二进制选择。
4.使用props.children
React具有一些特殊的属性,这些属性与其他属性有所不同。 需要使用这些key
来跟踪列表项的顺序。 另一个这样的特殊道具是children
。
您放置在组件的开始标记和结束标记之间的所有内容都放在props.children
内部。 并且您应该尽可能经常地使用它。
怎么了 因为这比为内容或仅需简单值(例如文本)的content
提供道具content
要容易得多。
<TableCell content="Some text" /> // <TableCell>Some text</TableCell>
使用props.children
有几个优点。 首先,这类似于常规HTML的工作方式。 其次,您可以随意转让任何东西! 无需在组件中添加诸如leftIcon
和rightIcon
类的道具-只需将它们作为props.children
一部分传递props.children
:
<TableCell> <ImportantIcon /> Some text </TableCell>
您可能会争辩说,您的组件仅需要呈现纯文本,在某些情况下是这样。 直到某个时候。 使用props.children
,您可以确保您的API可以满足不断变化的需求。
5.让父母坚持内部逻辑
有时,我们创建具有很多内部逻辑和状态的组件-例如,自动完成或交互式图表。
这样的组件经常遭受过多的API的困扰,其原因之一是随着项目的发展而积累了大量的不同用例。
但是,如果我们只提供一个标准化的道具,使开发人员可以控制,响应或简单地更改组件的默认行为,该怎么办?
肯特·多德斯(Kent Dodds)写了一篇关于状态简化器概念的出色文章。 这里是有关概念本身的文章 ,也是关于如何在React钩子中实现这一 概念的文章。
简而言之,这是到组件的状态减少器功能转移模式,这将使开发人员可以访问在组件内部执行的所有操作。 您可以更改状态,甚至引起副作用。 这是无需任何道具即可提供高水平定制的好方法。
可能是这样的:
function MyCustomDropdown(props) { const stateReducer = (state, action) => { if (action.type === Dropdown.actions.CLOSE) { buttonRef.current.focus(); } }; return ( <> <Dropdown stateReducer={stateReducer} {...props} /> <Button ref={buttonRef}>Open</Button> </> }
顺便说一句,您可以创建更简单的方式来响应事件。 在上一个示例中使用onClose
可能会使使用该组件更加方便。 必要时使用“状态减少器”模式。
6.对其余道具使用价差运算符
每次创建新组件时,请确保将省略号应用于其余道具,并将其发送到有意义的元素。
您无需继续向组件添加道具,只需将其传递给基本组件或元素即可。 这将使您的API更加稳定,在下一位开发人员需要新的事件侦听器或ARIA标签时,无需进行许多小版本错误。
您可以这样做:
function ToolTip({ isVisible, ...rest }) { return isVisible ? <span role="tooltip" {...rest} /> : null; }
每当您的组件将prop传递给实现时(例如类名或onClick
处理程序),请确保其他开发人员可以执行相同的操作。 对于类,您可以简单地使用方便的npm classnames库 (或者只是字符串连接)添加prop类:
import classNames from 'classnames'; function ToolTip(props) { return ( <span {...props} className={classNames('tooltip', props.tooltip)} /> }
对于单击处理程序和其他回调,可以使用一个小的实用程序将它们组合为一个函数。 这是执行此操作的一种方法:
function combine(...functions) { return (...args) => functions .filter(func => typeof func === 'function') .forEach(func => func(...args)); }
在这里,我们创建一个函数,该函数接受用于组合它们的函数列表。 它返回一个新的回调,该回调使用相同的参数依次调用它们。
此功能可以通过以下方式使用:
function ToolTip(props) { const [isVisible, setVisible] = React.useState(false); return ( <span {...props} className={classNames('tooltip', props.className)} onMouseIn={combine(() => setVisible(true), props.onMouseIn)} onMouseOut={combine(() => setVisible(false), props.onMouseOut)} /> ); }
7.使用默认值
确保为道具提供足够的默认值(默认值)。 因此,您将减少强制性道具的数量。 这将大大简化您的API。
以onClick
处理程序为例。 如果您的代码不需要此处理程序,请使用空函数(noop-function)作为默认属性。 这样,您就可以像始终传递代码一样在代码中调用它。
另一个示例可能是用户输入。 除非另外指定,否则假定输入字符串为空字符串。 这将使您确保始终处理字符串对象,而不是未定义或为null的对象。
8.无需重命名HTML属性
HTML作为一种语言有其自己的道具或属性,它本身就是HTML元素的API。 那么,为什么不继续使用此API呢?
如前所述,最小化API表面及其直观性是改善组件API的有用方法。 因此,为什么不使用现有的aria-label
而不是创建自己的screenReaderLabel
道具?
不要为了自己的“易用性”而将任何现有的HTML属性重命名 。 您甚至不必替换现有的API,只需在其之上添加自己的API。 人们仍然可以将aria-label
与screenReaderLabel属性一起传递-那么最终值应该是多少?
另外,请确保不要在组件中覆盖HTML属性。 一个很好的例子是<button />
元素的type
属性。 可以submit
(默认), button
或reset
。 但是,许多开发人员重新定义了此道具以表示按钮的视觉类型( primary
, cta
等)。
如果要使用这样的道具,则需要为true type
属性添加替代。 这将导致开发人员的困惑,怀疑和烦恼。
相信我-我一次又一次犯了这个错误-如果您犯了这个错误,那么您将不得不长时间解决它。
9.写出道具的类型(或只是类型)
没有任何文档能比您代码中包含的文档更好。 React提供了一种使用prop-types
包声明API的好方法。 使用它。
您可以为强制性和可选道具指定任何格式要求,并可以使用JSDoc comments对其进行改进。
如果您未指定强制性道具或传递无效或意外值,则在运行时,您将在控制台中收到警告。 这在开发过程中有很大帮助,可以从生产中删除。
如果您使用TypeScript或使用Flow编写React应用程序,则会获得API文档作为语言功能。 这进一步增强了开发工具的支持并简化了工作。
如果您自己不使用类型化的JavaScript,则仍应考虑向使用它的开发人员提供类型定义。 这样一来,他们使用您的组件将变得更加容易。
10.为开发人员设计
最后,要遵循的最重要的规则。 确保为使用API的开发人员优化了API和与组件一起使用。
简化开发人员工作的一种方法是向他提供有关不当使用的反馈。 仅在开发过程中,使用错误消息进行警告,并且仅在开发过程中执行此操作,并发出警告,指出存在使用组件的更有效方法。
编写错误和警告时,请提供文档链接或显示简单的代码示例。 开发人员越快地了解问题所在以及如何解决问题,组件工作起来就越方便。
令人难以置信的是,但事实证明,所有这些长错误警告的存在并不影响最终包装的大小。 由于消除了死代码的奇迹,所有这些文本和错误代码都可以在生产中的组装过程中删除。
可提供令人难以置信的良好反馈的库之一是React本身。 忘记为列表项指定键,写错生命周期方法,忘记扩展基类或以不确定的方式调用钩子都没关系-无论如何,您将在控制台中收到大量的错误消息。 为什么使用您的组件的开发人员对您的期望会更低?
因此,为您的未来用户设计。 为未来设计。 为不幸的人设计,这些人在离开时必须维护您的代码! 为开发人员设计。
合计
我们可以从经典的API方法中学到很多东西。 遵循本文的提示,技巧,规则和诫命,您可以创建易于使用,易于维护,直观并且在必要时非常灵活的组件。