Arrêtez de construire vos composants comme ça ❌

Arrêtez de construire vos composants comme ça ❌

Image de couverture pour Arrêtez de créer vos composants d'interface utilisateur comme celui-ci❌

En effet, tout le monde est heureux d'abstraire la logique commune en composants réutilisables. Mais des abstractions simples et bâclées peuvent aussi se retourner contre nous.Bien sûr, c'est un autre sujet.Aujourd'hui, nous allons discuter de la façon de concevoir des composants vraiment réutilisables.

Habituellement, nous réduisons les composants en définissant certains paramètres. Et, il y a de fortes chances que vous ayez aussi vu des composants dits "réutilisables" avec plus de 50 paramètres ! De tels composants finiraient par être difficiles à utiliser et à entretenir, tout en introduisant des problèmes de performances et des bogues difficiles à suivre.

Ajouter un paramètre pour répondre à de nouvelles exigences n'est pas aussi simple que d'écrire un morceau de iflogique , vous finirez par ajouter beaucoup de code et le composant deviendra très volumineux et difficile à maintenir.

Cependant, si l'on fait attention à la conception de composants abstraits, on peut écrire des composants vraiment faciles à utiliser et à maintenir, sans bugs stupides, et sans être trop compliqués pour être rédhibitoires pour les utilisateurs.

Kent C dodd's a une analyse approfondie de ce problème, voir : Simply React

À quoi ressemble un composant réutilisable ?

Voici un LoginFormModalcomposant qui résume les modaux pour les formulaires de connexion et d'inscription. Le composant lui-même n'est pas si complexe, n'acceptant que quelques propriétés, mais il est très rigide. Il peut y avoir beaucoup de modaux à créer dans notre application, nous voulons donc un composant plus flexible.

<LoginFormModal
  onSubmit={handleSubmit}
  modalTitle="Modal title"
  modalLabelText="Modal label (for screen readers)"
  submitButton={<button>Submit form</button>}
  openButton={<button>Open Modal</button>}
/>

复制代码

最后,我们将创建可以像这样被使用的组件:

<Modal>
  <ModalOpenButton>
    <button>Open Modal</button>
  </ModalOpenButton>
  <ModalContents aria-label="Modal label (for screen readers)">
    <ModalDismissButton>
      <button>Close Modal</button>
    </ModalDismissButton>
    <h3>Modal title</h3>
    <div>Some great contents of the modal</div>
  </ModalContents>
</Modal>
复制代码

但是,这并不是从代码量上看起来更复杂了。我们已将控制组件行为的能力赋予给了组件的使用者而不是创建者,这称为控制反转。它肯定比我们现有的 LoginFormModal 组件有更多的代码,但它更简单,更灵活,适合我们未来的用例,而且不会变得更加复杂。

例如,考虑这样一种情况:我们不想只渲染表单,而是想要渲染我们喜欢的任何内容。我们的 Modal 支持这一点,但 LoginFormModal 需要接受一个新的参数。或者,如果我们希望关闭按钮显示在内容的下方,该怎么办?我们需要一个名为 renderCloseBelow 的特殊参数。但是对于我们的 Modal,这显而易见可以轻松做到。你只需将 ModalCloseButton 组件移动到所需的位置即可。

更加灵活,更少的接口暴露。

这种模型称为复合组件 - 多个组件组合成所需的 UI。典型的例子是 HTML 中的 <select><option>

它广泛用于许多实际的库中,例如:

让我们创建第一个复合组件,同时创建一个可重用的 modal

创建我们的第一个复合组件

import * as React from 'react'
import VisuallyHidden from '@reach/visually-hidden'

/* Here the Dialog and CircleButton is a custom component 
Dialog is nothing button some styles applied on reach-dialog 
component provided by @reach-ui */
import {Dialog, CircleButton} from './lib'

const ModalContext = React.createContext()
//this helps in identifying the context while visualizing the component tree
ModalContext.displayName = 'ModalContext'

function Modal(props) {
  const [isOpen, setIsOpen] = React.useState(false)

  return <ModalContext.Provider value={[isOpen, setIsOpen]} {...props} />
}

function ModalDismissButton({children: child}) {
  const [, setIsOpen] = React.useContext(ModalContext)
  return React.cloneElement(child, {
    onClick: () => setIsOpen(false),
  })
}

function ModalOpenButton({children: child}) {
  const [, setIsOpen] = React.useContext(ModalContext)
  return React.cloneElement(child, {
    onClick: () => setIsOpen(true),
})
}

function ModalContentsBase(props) {
  const [isOpen, setIsOpen] = React.useContext(ModalContext)
  return (
    <Dialog isOpen={isOpen} onDismiss={() => setIsOpen(false)} {...props} />
  )
}

function ModalContents({title, children, ...props}) {
  return (
    //we are making generic reusable component thus we allowed user custom styles
   //or any prop they want to override
    <ModalContentsBase {...props}>
      <div>
        <ModalDismissButton>
          <CircleButton>
            <VisuallyHidden>Close</VisuallyHidden>
            <span aria-hidden>×</span>
          </CircleButton>
        </ModalDismissButton>
      </div>
      <h3>{title}</h3>
      {children}
    </ModalContentsBase>
  )
}

export {Modal, ModalDismissButton, ModalOpenButton, ModalContents}
复制代码

耶!我们实现了很多的逻辑了,现在可以使用上面的组件,例如:

<Modal>
     <ModalOpenButton>
         <Button>Login</Button>
     </ModalOpenButton>
     <ModalContents aria-label="Login form" title="Login">
         <LoginForm
            onSubmit={register}
            submitButton={<Button>Login</Button>}
          />
      </ModalContents>
  </Modal>
复制代码

现在代码更具可读性和灵活性了。

code élégant et magnifique

允许用户传递自己定义的 onClickHandler

ModalOpenButtonModalCloseButton 设置其子按钮的 onClick 事件,以便我们可以打开和关闭模态框。但是,如果这些组件的用户想要在用户单击按钮时执行某些操作(除了打开/关闭模态框之外)(例如:触发分析业务),该怎么办?

我们创建一个 callAll 方法,它执行传递给它的所有方法,如下:

callAll(() => setIsOpen(false), ()=>console.log("I ran"))
复制代码

我从 Kent 的 Epic React workshop 中学到了这一点。这太聪明了,我喜欢它。

const callAll = (...fns) => (...args) => fns.forEach(fn => fn && fn(...args))
复制代码

让我们在我们的组件中使用它:

function ModalDismissButton({children: child}) {
  const [, setIsOpen] = React.useContext(ModalContext)
  return React.cloneElement(child, {
    onClick: callAll(() => setIsOpen(false), child.props.onClick),
  })
}

function ModalOpenButton({children: child}) {
  const [, setIsOpen] = React.useContext(ModalContext)
  return React.cloneElement(child, {
    onClick: callAll(() => setIsOpen(true), child.props.onClick),
  })
}
复制代码

这让我们可以通过将 onClickHandler 传递给我们的自定义按钮来使用,如下所示:

<ModalOpenButton>
  <button onClick={() => console.log('sending data to facebook ;)')}>Open Modal</button>
</ModalOpenButton>
复制代码

Célébrer le gars

总结

不要匆忙地就进行组件的抽象,也不要把一切都留给参数。也许它现在是一个简单的组件,但你不知道将来需要实现哪些用例,不要认为这是时间和可维护性之间的权衡,复杂性可能会呈指数级增长。

在 React 中发挥复合组件的优势,让你的生活更轻松。

另外,请查看 Kent 的 Epic React Course,在那里我了解到了复合组件模式(Compound Components)等等。

关于我,我叫 Harsh,我喜欢写代码。我从 16 岁开始就一直在做这件事。在使用 React 构建 Web 应用程序时,我感到宾至如归。我目前正在学习 Remix

如果你喜欢这篇博客,那关注我吧!我正计划分享更多习惯的内容。

Twitter
Linkedin

进一步了解我: Harsh choudhary

欢迎阅读其他博客 测试你的 hook 或者 如何编写自定义 hook.

Le projet de traduction Nuggets est une communauté qui traduit des articles techniques Internet de haute qualité. La source des articles est les articles de partage en anglais sur Nuggets. Le contenu couvre Android , iOS , front-end , back- end , blockchain , produits , design , intelligence artificielle et d'autres domaines. Si vous souhaitez voir plus de traductions de haute qualité, veuillez continuer à prêter attention au plan de traduction Nuggets , officiel Colonne Weibo et Zhihu .

Je suppose que tu aimes

Origine juejin.im/post/7085634227177259022
conseillé
Classement