React组件的十诫

十诫


Kristofer SelbekkCaroline Odden合作撰写。 基于2019年6月在奥斯陆举行的ReactJS会议上同名同人的演讲。

从翻译者那里看, 《十个组件诫命》的原始名称没有提到React,但是大多数示例和建议都专门涉及到React,此外,本文被放置在react标签下,并且开发人员编写了react


创建许多开发人员将要使用的组件并不容易。 如果这些道具是公共API的一部分,则必须仔细考虑要使用哪些道具。


在本文中,我们将简要介绍一些用于整体开发API的最佳实践,并形成十个可用于创建开发人员乐于使用的组件的命令。


api


什么是API?


API(或应用程序编程接口)是两个代码相遇的地方。 这是您的代码与世界其他地方之间的接触面。 我们称此表面为界面。 这是可以与之交互的一组特定的操作或数据点。


该类与调用该类的代码之间的接口也是API。 您可以调用该类的方法来接收数据或运行其中包含的函数。


遵循相同的原则, 您的组件接受道具,这就是它的API 。 这是开发人员与您的组件进行交互的方式。


一些最佳的API设计实践


那么,开发API时应遵循哪些规则和注意事项? 好吧,我们进行了一些研究,结果发现,在这个问题上有很多宝贵的资源。 我们选择了两个-Josh Tauberer- “什么才是好的API?”罗恩·库里尔Ron Kurir)的同名文章-谈到了这四种做法。


稳定版本


创建API时要考虑的最重要的事情之一就是使其保持尽可能的稳定。 关键更改的数量应最少。 如果必须进行重大更改,请确保编写详细的更新指南,并在可能的情况下,编写对开发人员而言可自动执行此过程的mod代码。


描述性错误消息


每当在调用API时发生错误时,您都应该尽一切可能解释发生了什么问题以及如何解决。 如果您用“滥用”之类的消息责骂用户,并且不给出任何解释,则您的API会留下不好的印象。


而是编写描述性错误,以帮助用户修复他们如何使用您的API。


减少开发人员的意外


开发人员是脆弱的生物,当他们使用您的API时,您不应惊吓它们。 换句话说,使您的API尽可能直观。 如果遵循最佳做法和现有的命名约定,则将实现此目的。


另外,您的代码应始终保持一致。 如果在一个位置使用ishas的属性的逻辑名称,但将其进一步跳过,则会使人们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的工作方式。 其次,您可以随意转让任何东西! 无需在组件中添加诸如leftIconrightIcon类的道具-只需将它们作为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 (默认), buttonreset 。 但是,许多开发人员重新定义了此道具以表示按钮的视觉类型( primarycta等)。


如果要使用这样的道具,则需要为true type属性添加替代。 这将导致开发人员的困惑,怀疑和烦恼。


相信我-我一次又一次犯了这个错误-如果您犯了这个错误,那么您将不得不长时间解决它。


9.写出道具的类型(或只是类型)


没有任何文档能比您代码中包含的文档更好。 React提供了一种使用prop-types包声明API的好方法。 使用它。


您可以为强制性和可选道具指定任何格式要求,并可以使用JSDoc comments对其进行改进。


如果您未指定强制性道具或传递无效或意外值,则在运行时,您将在控制台中收到警告。 这在开发过程中有很大帮助,可以从生产中删除。


如果您使用TypeScript或使用Flow编写React应用程序,则会获得API文档作为语言功能。 这进一步增强了开发工具的支持并简化了工作。


如果您自己不使用类型化的JavaScript,则仍应考虑向使用它的开发人员提供类型定义。 这样一来,他们使用您的组件将变得更加容易。


10.为开发人员设计


最后,要遵循的最重要的规则。 确保为使用API​​的开发人员优化了API和与组件一起使用。


简化开发人员工作的一种方法是向他提供有关不当使用的反馈。 仅在开发过程中,使用错误消息进行警告,并且仅在开发过程中执行此操作,并发出警告,指出存在使用组件的更有效方法。


编写错误和警告时,请提供文档链接或显示简单的代码示例。 开发人员越快地了解问题所在以及如何解决问题,组件工作起来就越方便。


令人难以置信的是,但事实证明,所有这些长错误警告的存在并不影响最终包装的大小。 由于消除了死代码的奇迹,所有这些文本和错误代码都可以在生产中的组装过程中删除。


可提供令人难以置信的良好反馈的库之一是React本身。 忘记为列表项指定键,写错生命周期方法,忘记扩展基类或以不确定的方式调用钩子都没关系-无论如何,您将在控制台中收到大量的错误消息。 为什么使用您的组件的开发人员对您的期望会更低?


因此,为您的未来用户设计。 为未来设计。 为不幸的人设计,这些人在离开时必须维护您的代码! 为开发人员设计。


合计


我们可以从经典的API方法中学到很多东西。 遵循本文的提示,技巧,规则和诫命,您可以创建易于使用,易于维护,直观并且在必要时非常灵活的组件。

Source: https://habr.com/ru/post/zh-CN460559/


All Articles