写一个公用的组件很难,你必须细心地考虑不少问题,好比应该暴露出哪些 props
。react
本文将简要的介绍 API 设计中的一些最佳实践,以及编写 React 组件的 10 条参考规则。后端
API (Application Programming Interface)是两段代码或者两个应用如何交互的一个定义或者接口。数组
同理,组件定义的 props
也是 API,这是用户与组件交互的方式。ide
因此,设计一个 API 的时候应该有哪些规则和注意事项?咱们作了不少研究并结合实践给出了 4 条 API 设计的最佳实践:函数
最重要的规则之一就是要保持稳定,意思就是要最大限度的减小破坏性的升级,若是你作了破坏性的升级,必定要写一个完整的升级指南,若是可能的话,再提供一些 API 让用户消化升级的过程,使用户升级版本的成本下降。工具
若是你要发布 API,请使用 语义化版本,以便用户能够自由的选择他们须要的版本。post
调用 API 出错的时候,返回给客户端一个详细的错误说明,而且告诉客户端应该如何解决。在没有任何上下文的状况下返回一个 “调用出错” 的错误信息给客户端并非一个友好的体验。学习
开发人员都是很傲娇的,并不想在使用你 API 的时候感到迷惑,换句话说,使你的 API 尽量的直观,规范。能够经过遵循一些原则和命名规范去实现。优化
举个例子,你的 API 提供了 boolean 类型的参数,参数命名的时候你在一个地方用 is 作前缀,另一个地方又用了 has 作前缀,而后其余地方又用了另一个前缀,这会让开发人员比较迷惑。
固然不是说功能多了很差,只是要善用外观模式或者命令模式等去封装一些操做,作到高内聚,API 过多会增长学习成本,一个高内聚的 API 会被认作是一个易于使用的 API。
上面 4 条规则在 REST APIs 中应用的很好,以前提过的,咱们的组件也有它的 API,就是 props
,咱们该如何定义 props
使它不违反上面的规则呢?下面列出 10 条建议:
若是你没有写个组件的使用文档,好吧,使用者能够看你的代码,但这并非一个好的体验。
有不少写文档的工具,这里推荐三个:
无论选用哪一个,必定要把全部的 API 使用说明都写出来。
HTML是一种以语义方式结构化信息的语言。然而,咱们的大多数组件都是由 <div />
标签组成的。它在某种程度上是没问题的,由于通用组件不知道咱们想要的是 <article />
或 <section />
仍是 <aside />
,可是这样并不优雅。因此,咱们建议容许组件接受一个 prop
,它将覆盖正在呈现的 DOM 元素。如下是如何实现它的示例:
function Grid({ as: Element, ...props }) {
return <Element className="grid" {...props} /> } Grid.defaultProps = { as: 'div', }; 复制代码
在 jsx 中把 as
prop 重命名文本地变量 Element
,而且把默认值设为 div
。
使用 <Grid />
的时候传递你想要的标签名就行了。
function App() {
return (
<Grid as="main"> <MoreContent /> </Grid>
);
}
复制代码
这样在 React 中使用是没有问题的,另一个经典的例子就是你有一个 <Button />
组件,想把它渲染成 React Router 的 <Link />
<Button as={Link} to="/profile">
Go to Profile
</Button>
复制代码
Boolean props 听起来是个不错的选择,由于你能够不用指定值,看起来很是优雅。
<Button large>BUY NOW!</Button>
复制代码
尽管 Boolean props 看起来很是优雅,可是它只能支持两个值 true
or false
若是你的组件设计了不少种 boolean 类型的 props 看起来就比较累赘了:
<Button large small primary disabled secondary>
WHAT AM I??
</Button>
复制代码
换句话说,boolean 一般很难根据需求去扩展。换成字符串枚举是一个更好的选择,它能够扩展为二元值以外的任意值。
<Button variant="primary" size="large">
I am primarily a large button
</Button>
复制代码
不是说 boolean props 一无可取,一些不可能扩展而且只有两个值的 prop 好比 disabled 仍是用 boolean 类型。
React 有几个特殊的 prop,好比 key
,他们有特殊的处理机制,还有一个就是 children
。
在开始标签和结束标签中间的内容都会被塞进 props.children
props,应该尽量多的使用它,由于它比一个 content
prop,或者一些文本内容须要传递的时候更易使用。
<TableCell content="Some text" />
// vs
<TableCell>Some text</TableCell>
复制代码
使用 props.children
有几个好处。首先,它相似于常规 HTML 的使用方式。其次,你能够自由地传递任何你想要的东西,而不是将 leftIcon
和 rightIcon
prop 添加到组件中,只需将它们做为 props.children
prop 的一部分传递:
<TableCell>
<ImportantIcon /> Some text </TableCell>
复制代码
你可能会说你的组件只会渲染纯文本,不须要其余东西,如今看来可能没问题,可是之后需求不断变化的时候你就会发现 props.children
的好处。
有时候咱们会写一些内部逻辑很复杂的组件,好比 AutoComplete
或者一些图表。
这些类型的组件要渲染的内容一般依赖外部的 API,随着时间的推移,可能还要实现一些特殊的需求
咱们如何提供一个单一又标准化的 prop 让调用者去控制或者覆盖组件内部的默认逻辑呢?
解决方案是 state reducers
模式,这里有一篇文章 post about the concept itself
总结下来,state reducer
模式就是让消费者能够访问到组件内部发生的一切事件、状态,从而进行更高级的自定义配置
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>
</>
}
复制代码
当你建立一个新组件的时候,确保剩余的 props 被传递到能够生效的 element 上,没必要仅仅是为了向底层组件传递 props 就额外添加一个。能够用 ...rest
操做符:
function ToolTip({ isVisible, ...rest }) {
return isVisible ? <span role="tooltip" {...rest} /> : null; } 复制代码
尽可能为 props 提供默认值,这样也能够大大减小须要传递 props 的数量,以 onClick
为例,能够提供一个空函数做为默认值,另外能够为字符类型的 prop 提供一个空字符串做为默认值,这样当没有传递 prop 的时候就能够确保处理的是空字符串而不是 undefined 或者 null 等不肯定的值
HTML 标签自己就含有本身的一些属性,就是他本身的 API,为何不用呢?
就像前面提到过的,减小暴露的 API,为何要添加一个 screenReaderLabel prop 而不用自身原有的 aria-label API呢?
因此,请不要为了“易用性”而去重复定义,好比添加了 screenReaderLabel prop,而后又传递了一个 aria-label 属性,那么最终显示应该是什么呢?
另外,请不要覆盖 HTML 标签自己的属性,好比 <button />
元素的 type 属性,能够是 submit(默认)button 或者 reset,然而许多开发者都将它从新定义为其余的含义的props(primary warning info 等),这样就令使用者比较迷惑。
代码既文档,如今已经有 prop-types
包可用,去使用它。
若是没有传递一些必须的 props,控制台会报错,若是用的是 TypeScript 或者 Flow,那开发体验就更好了。
最后,遵循最重要的规则。确保您的 API 和 “组件体验” 针对将使用它的人员或是开发同事进行了优化。
提高开发体验的一个方法就是提供详细的报错信息,或者是在开发模式下在控制台发出警告。
在控制台报错或者报警的时候,若是开发人员也看到了对应错误或者警告的文档连接,会大大提高组件的使用体验。
没必要担忧过于冗长的错误信息会占用太大空间,何况构建生产环境的时候也不会把这些信息打包进去。
React 自己就是一个很是优秀的类库,当你忘记使用 key 或者拼错了生命周期的名字等等,都会在控制台收到大量详细的错误警告信息。