从构建 React 组件中学到的两件事

Avatar of Robin Rendle
Robin Rendle

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费额度!

以下是我在如何构建 React 组件方面学到的两件事。这些是我在过去几个月里遇到的问题,如果您正在构建设计系统,特别是那些包含大量遗留技术决策和大量技术债务的设计系统,您可能会对它们感兴趣。

第一课:尽可能避免子组件

在大型设计系统中使用大量组件时,以下模式最终会很快变得有問題

<Card>
  <Card.Header>Title</Card.Header>
  <Card.Body><p>This is some content</p></Card.Body>
</Card>

有问题的部分是这些子组件,Card.BodyCard.Header。这个例子并不糟糕,因为事情相对简单——当组件变得更复杂时,事情可能会变得很混乱。例如,每个子组件都可以有一系列复杂的 props,这些 props 会相互干扰。

我最大的痛点之一是我们的表单组件。比如这个

<Form>
  <Input />
  <Form.Actions>
    <Button>Submit</Button>
    <Button>Cancel</Button>
  </Form.Actions>
</Form>

当然,我简化了很多东西,但是每次工程师想要将两个按钮并排放置时,他们都会导入Form.Actions,即使页面上没有Form。这意味着Form组件中的所有内容都会被导入,这最终不利于性能。它碰巧也是糟糕的系统设计实现。

这在编写组件文档时也会使事情变得更加困难,因为现在您还必须确保这些子组件也都有文档。

因此,与其将Form.Actions作为子组件,不如将其作为一个全新的组件,简单地命名为:FormActions(或者可能是更好的名称,例如ButtonGroup)。这样,我们就不必一直导入Form,并且可以将基于布局的组件与其他组件分开。

我已经吸取了教训。从现在开始,我会尽可能避免使用子组件。

第二课:确保您的 props 不会相互冲突

Mandy Michael 撰写了一篇关于props 如何相互碰撞并导致各种令人困惑的冲突的优秀文章,例如以下 TypeScript 示例

interface Props {
  hideMedia?: boolean
  mediaIsEdgeToEdge?: boolean
  mediaFullHeight?: boolean
  videoInline?: boolean
}

Mandy 写道

这些 props 的目的是更改卡片中图像或视频的渲染方式,或者是否渲染媒体。单独定义它们的问题在于,您最终会得到许多切换组件功能的标志,其中许多是互斥的。例如,如果图像也隐藏了,则不能使其填充边距。

这绝对是我们团队设计系统中继承的大量组件存在的问题。有很多组件,其中布尔型 props 会使组件以各种奇怪且意想不到的方式运行。在开发过程中,我们的Card组件甚至出现过各种错误,因为工程师不知道要为任何给定效果打开和关闭哪些 props!

Mandy 提供了以下解决方案

type MediaMode = 'hidden'| 'edgeToEdge' | 'fullHeight'

interface Props {
  mediaMode: 'hidden'| 'edgeToEdge' | 'fullHeight'
}

简而言之:如果我们将所有这些新兴选项组合在一起,那么我们将拥有一个更简洁的API,该 API 易于扩展,并且不太可能在将来造成混淆。


就是这样!我只想快速记录一下这两点。我的问题是:在构建组件或构建设计系统时,您学到了什么?