[译] 编写 React 组件的 10 个建议

原文地址:The 10 Component Commandments前端

写一个公用的组件很难,你必须细心地考虑不少问题,好比应该暴露出哪些 propsreact

本文将简要的介绍 API 设计中的一些最佳实践,以及编写 React 组件的 10 条参考规则。后端

什么是 API ?

API (Application Programming Interface)是两段代码或者两个应用如何交互的一个定义或者接口。数组

  • 后端和前端交互使用的是就是 API,能够经过此 API 获取或者操做一组数据
  • 类和调用该类的代码之间的接口也是 API,你能够调用类里面的方法

同理,组件定义的 props 也是 API,这是用户与组件交互的方式。ide

API 设计中的一些最佳实践

因此,设计一个 API 的时候应该有哪些规则和注意事项?咱们作了不少研究并结合实践给出了 4 条 API 设计的最佳实践:函数

稳定的版本

最重要的规则之一就是要保持稳定,意思就是要最大限度的减小破坏性的升级,若是你作了破坏性的升级,必定要写一个完整的升级指南,若是可能的话,再提供一些 API 让用户消化升级的过程,使用户升级版本的成本下降。工具

若是你要发布 API,请使用 语义化版本,以便用户能够自由的选择他们须要的版本。post

提供错误描述信息

调用 API 出错的时候,返回给客户端一个详细的错误说明,而且告诉客户端应该如何解决。在没有任何上下文的状况下返回一个 “调用出错” 的错误信息给客户端并非一个友好的体验。学习

不要让开发人员迷惑

开发人员都是很傲娇的,并不想在使用你 API 的时候感到迷惑,换句话说,使你的 API 尽量的直观,规范。能够经过遵循一些原则和命名规范去实现。优化

举个例子,你的 API 提供了 boolean 类型的参数,参数命名的时候你在一个地方用 is 作前缀,另一个地方又用了 has 作前缀,而后其余地方又用了另一个前缀,这会让开发人员比较迷惑。

暴露出来的 API 尽量的少

固然不是说功能多了很差,只是要善用外观模式或者命令模式等去封装一些操做,作到高内聚,API 过多会增长学习成本,一个高内聚的 API 会被认作是一个易于使用的 API。

组件设计的 10 个建议

上面 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

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 类型。

使用 props.children

React 有几个特殊的 prop,好比 key,他们有特殊的处理机制,还有一个就是 children

在开始标签和结束标签中间的内容都会被塞进 props.children props,应该尽量多的使用它,由于它比一个 content prop,或者一些文本内容须要传递的时候更易使用。

<TableCell content="Some text" />
// vs
<TableCell>Some text</TableCell>
复制代码

使用 props.children 有几个好处。首先,它相似于常规 HTML 的使用方式。其次,你能够自由地传递任何你想要的东西,而不是将 leftIconrightIcon 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

当你建立一个新组件的时候,确保剩余的 props 被传递到能够生效的 element 上,没必要仅仅是为了向底层组件传递 props 就额外添加一个。能够用 ...rest 操做符:

function ToolTip({ isVisible, ...rest }) {
  return isVisible ? <span role="tooltip" {...rest} /> : null; } 复制代码

给出足够的默认值

尽可能为 props 提供默认值,这样也能够大大减小须要传递 props 的数量,以 onClick 为例,能够提供一个空函数做为默认值,另外能够为字符类型的 prop 提供一个空字符串做为默认值,这样当没有传递 prop 的时候就能够确保处理的是空字符串而不是 undefined 或者 null 等不肯定的值

不要重命名 HTML 的属性

HTML 标签自己就含有本身的一些属性,就是他本身的 API,为何不用呢?

就像前面提到过的,减小暴露的 API,为何要添加一个 screenReaderLabel prop 而不用自身原有的 aria-label API呢?

因此,请不要为了“易用性”而去重复定义,好比添加了 screenReaderLabel prop,而后又传递了一个 aria-label 属性,那么最终显示应该是什么呢?

另外,请不要覆盖 HTML 标签自己的属性,好比 <button /> 元素的 type 属性,能够是 submit(默认)button 或者 reset,然而许多开发者都将它从新定义为其余的含义的props(primary warning info 等),这样就令使用者比较迷惑。

编写 prop types

代码既文档,如今已经有 prop-types 包可用,去使用它。

若是没有传递一些必须的 props,控制台会报错,若是用的是 TypeScript 或者 Flow,那开发体验就更好了。

为开发者设计

最后,遵循最重要的规则。确保您的 API 和 “组件体验” 针对将使用它的人员或是开发同事进行了优化。

提高开发体验的一个方法就是提供详细的报错信息,或者是在开发模式下在控制台发出警告。

在控制台报错或者报警的时候,若是开发人员也看到了对应错误或者警告的文档连接,会大大提高组件的使用体验。

没必要担忧过于冗长的错误信息会占用太大空间,何况构建生产环境的时候也不会把这些信息打包进去。

React 自己就是一个很是优秀的类库,当你忘记使用 key 或者拼错了生命周期的名字等等,都会在控制台收到大量详细的错误警告信息。

相关文章
相关标签/搜索