Maison >interface Web >js tutoriel >[Français] Refactorisation des composants React à l'aide de hooks personnalisés

[Français] Refactorisation des composants React à l'aide de hooks personnalisés

青灯夜游
青灯夜游avant
2023-01-17 20:13:511258parcourir

[Français] Refactorisation des composants React à l'aide de hooks personnalisés

J'entends souvent des gens parler des composants de fonction React, mentionnant que les composants de fonction deviendront inévitablement plus grands et auront une logique plus complexe. Après tout, nous avons écrit le composant dans "une fonction", vous devez donc accepter que le composant se développera et que la fonction continuera à se développer. C'est également mentionné dans les composants React :

Étant donné que les composants de fonction peuvent faire de plus en plus de choses, les composants de fonction dans votre base de code deviendront globalement de plus en plus longs. [Recommandations associées : Tutoriel vidéo Redis, Vidéo de programmation]

Il est également mentionné que nous devrions :

Essayez d'éviter d'ajouter des abstractions prématurément

Si vous utilisez CodeScene, vous remarquerez peut-être qu'il avertira vous lorsque votre fonction est trop longue ou complexe. Si nous suivons ce que nous avons dit précédemment, nous pourrions nous demander si nous devrions configurer plus largement les avertissements liés à CodeScene. Bien sûr, cela peut être fait, mais je pense que nous ne devrions pas le faire, et nous ne devrions pas refuser d'ajouter beaucoup d'abstractions au code. Nous pouvons en tirer de nombreux avantages, et la plupart du temps, le coût n'est pas élevé. haut. Nous pouvons continuer à maintenir la santé de notre code en très bonne qualité !

Gérer la complexité

Nous devons réaliser que bien que le composant fonction soit écrit dans "une fonction", cette fonction peut toujours être composée de nombreuses autres fonctions comme d'autres fonctions. Comme useState, useEffect ou d'autres hooks, les sous-composants eux-mêmes sont également des fonctions. Par conséquent, nous pouvons naturellement utiliser la même idée pour traiter la complexité des composants de fonction : useStateuseEffect ,抑或是别的hooks,子组件它们本身也是个函数。因此我们自然可以利用相同的思路来处理函数组件的复杂性问题:通过建立一个新函数,来把即符合公共模式又复杂的代码封装起来

比较常见的处理复杂组件的方式是把它分解成多个子组件。但是这么做可能会让人觉得不自然或是很难准确的去描述这些子组件。这时候我们就可以借助梳理组件的钩子函数的逻辑来发现新的抽象点。

每当我们在组件内看到由useStateuseEffect 或是其他内置钩子函数组成的长长的列表时,我们就应该去考虑是否可以将它们提取到一个自定义hook中去。自定义hook函数是一种可以在其内部使用其他钩子函数的函数,并且创建一个自定义钩子函数也很简单。

如下所示的组件相当于一个看板,用一个列表展示一个用户仓库的数据(想像成和github类似的)。这个组件并不算是个复杂组件,但是它是展示如何应用自定义hook的一个不错的例子。

function Dashboard() {
  const [repos, setRepos] = useState<Repo[]>([]);
  const [isLoadingRepos, setIsLoadingRepos] = useState(true);
  const [repoError, setRepoError] = useState<string | null>(null);

  useEffect(() => {
    fetchRepos()
      .then((p) => setRepos(p))
      .catch((err) => setRepoError(err))
      .finally(() => setIsLoadingRepos(false));
  }, []);

  return (
    <div className="flex gap-2 mb-8">
      {isLoadingRepos && <Spinner />}
      {repoError && <span>{repoError}</span>}
      {repos.map((r) => (
        <RepoCard key={i.name} item={r} />
      ))}
    </div>
  );
}

我们要把钩子逻辑提取到一个自定义hook中,我们只需要把这些代码复制到一个以use 开头的函数中(在这里我们将其命名为useRepos):

/**
 * 请求所有仓库用户列表的hook函数
 */
export function useRepos() {
  const [repos, setRepos] = useState<Repo[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    fetchRepos()
      .then((p) => setRepos(p))
      .catch((err) => setError(err))
      .finally(() => setIsLoading(false));
  }, []);

  return [repos, isLoading, error] as const;
}

必须用use 开头的原因是linter 插件可以检测到你当前创建的是个钩子函数而不是普通函数,这样插件就可以检查你的钩子函数是否符合正确的自定义钩子的相关规则

相比提炼之前,提炼后出现的新东西只有返回语句as const 。这里的类型提示只是为了确保类型推断是正确的:一个包含3个元素的数组,类型分别是Repo[], boolean, string | null 。当然,你可以从钩子函数返回任何你希望返回的东西。

译者注:这里添加as const 在ts类型推断的区别主要体现在数字元素的个数。不添加as const ,推断的类型为(string | boolean | Repo[] | null)[],添加后的类型推断为readonly [Repo[], boolean, string | null]

将自定义钩子useRepos 应用在我们的组件中,代码变成了:

function Dashboard() {
  const [repos, isLoadingRepos, repoError] = useRepos();

  return (
    <div className="flex gap-2 mb-8">
      {isLoadingRepos && <Spinner />}
      {repoError && <span>{repoError}</span>}
      {repos.map((i) => (
        <RepoCard key={i.name} item={i} />
      ))}
    </div>
  );
}

可以发现,我们现在在组件内部无法调用任何的setter 函数,即无法改变状态。在这个组件我们已经不需要包含修改状态的逻辑,这些逻辑都包含在了useReposEn créant une nouvelle fonction, nous pouvons encapsuler un code complexe conforme à un modèle commun

.

La manière la plus courante de traiter des composants complexes est de les décomposer en plusieurs sous-composants. Mais cela peut sembler contre nature ou rendre difficile la description précise de ces sous-composants. À ce stade, nous pouvons découvrir de nouveaux points abstraits en triant la logique de la fonction hook du composant.

Chaque fois que nous voyons une longue liste de useState, useEffect ou d'autres fonctions de hook intégrées dans un composant, nous devons nous demander si nous pouvons les extraire dans un hook personnalisé. . Une fonction de hook personnalisée est une fonction qui peut utiliser d'autres fonctions de hook à l'intérieur, et la création d'une fonction de hook personnalisée est également simple.

🎜Le composant présenté ci-dessous est équivalent à un tableau de bord, utilisant une liste pour afficher les données d'un entrepôt d'utilisateurs (imaginez similaire à github). Ce composant n'est pas un composant complexe, mais c'est un bon exemple de la façon d'appliquer des hooks personnalisés. 🎜rrreee🎜Nous allons extraire la logique du hook dans un hook personnalisé, il nous suffit de copier ce code dans une fonction commençant par use (ici nous la nommerons useRepos ) : 🎜rrreee🎜doit commencer par use car le plug-in linter peut détecter que ce que vous êtes en train de créer est une fonction hook au lieu d'une fonction ordinaire, de sorte que le plug-in Vous pouvez vérifier si votre fonction de hook est conforme au hook personnalisé correctRègles associées 🎜. 🎜🎜Par rapport à avant le raffinage, les seules nouvelles choses qui sont apparues après le raffinage étaient les 🎜return déclarations🎜 et as const. L'indice de type ici sert simplement à garantir que l'inférence de type est correcte : un tableau contenant 3 éléments, les types sont Repo[], boolean, string | null. Bien sûr, vous pouvez renvoyer tout ce que vous souhaitez à partir de la fonction hook. 🎜🎜🎜Note du traducteur : ajoutez as const ici. La différence dans l'inférence de type ts se reflète principalement dans le nombre d'éléments numériques. Sans ajouter as const, le type déduit est (string | boolean | Repo[] | null)[], et l'inférence de type ajoutée est readonly [Repo[ ], booléen, chaîne | null]. 🎜🎜🎜Appliquez le hook personnalisé useRepos à notre composant, et le code devient : 🎜rrreee🎜Vous pouvez constater que nous ne pouvons plus appeler aucun setter à l'intérieur du composant. Les fonctions ne peuvent pas changer État. Dans ce composant, nous n'avons plus besoin d'inclure la logique pour modifier l'état. Ces logiques sont incluses dans la fonction hook useRepos. Bien sûr, si vous en avez vraiment besoin, vous pouvez les exposer dans l'instruction return de la fonction hook. 🎜🎜Quels sont les avantages de faire cela ? La documentation de React mentionne :🎜🎜🎜En extrayant des fonctions de hook personnalisées, la logique des composants peut être réutilisée🎜

Nous pouvons simplement imaginer que si d'autres composants de cette application doivent également afficher la liste des utilisateurs dans l'entrepôt, alors tout ce que ce composant doit faire est d'importer la fonction hook useRepos. Si le hook est mis à jour, peut-être en utilisant une forme de mise en cache, ou en étant continuellement mis à jour via une interrogation ou une approche plus complexe, alors tous les composants qui font référence à ce hook en bénéficieront. useRepos 钩子函数。如果钩子更新了,可能使用某种形式的缓存,或者通过轮询或更复杂的方法进行持续更新,那么引用了这个钩子的所有组件都将受益。

当然,提取自定义钩子除了可以方便复用外,还有别的好处。在我们的例子中,所有的useStateuseEffect 都是为了实现同一个功能——就是获取库用户列表,我们把这个看作一个原子功能,那么在一个组件中,包含很多个这样的原子功能也是很常见的。如果我们把这些原子功能的代码都分别提取到不同的自定义钩子函数中,就更容易发现哪些状态在我们修改代码逻辑时要保持同步更新,不容易出现遗漏的情况。除此之外,这么做的好处还有:

  • 越短小的函数越容易看懂
  • 为原子功能命名的能力(如useRepo)
  • 更自然的提供文档说明(每个自定义钩子函数的功能更加内聚单一,这种函数也很容易去写注释)

最后

我们已经了解到React的钩子函数并没有多么神秘,也和其他函数一样很容易就可以创建。我们可以创建自己的领域特定的钩子,进而在整个应用程序中重用。也可以在各种博客或“钩子库”中找到很多预先编写好的通用钩子。这些钩子可以想useStateuseEffect 一样很方便的在我们的项目中应用。Dan Abramov的useInterval钩子就是一个例子,例如你有一个类似于useRepos 的钩子,但是你需要可以轮询更新?那你就可以尝试在你的钩子中使用useInterval

Bien sûr, extraire des crochets personnalisés facilite non seulement la réutilisation, mais présente également d'autres avantages. Dans notre exemple, tous les useState et useEffect sont utilisés pour réaliser la même fonction - pour obtenir la liste des utilisateurs de la bibliothèque. Nous considérons cela comme une fonction atomique, alors c'est également courant. pour qu’un composant contienne plusieurs de ces fonctions atomiques. Si nous extrayons les codes de ces fonctions atomiques dans différentes fonctions de hook personnalisées, il sera plus facile de trouver quels états doivent être mis à jour de manière synchrone lorsque nous modifions la logique du code, et il sera moins probable qu'ils soient manqués. De plus, les avantages de cette procédure sont :
  • Les fonctions plus courtes sont plus faciles à comprendre
  • La possibilité de nommer des fonctions atomiques (telles que useRepo)
  • Fournir de la documentation plus naturellement (la fonction de chaque fonction hook personnalisée est plus cohérente et unique, et il est facile d'écrire des commentaires pour ce type de fonction)

Enfin

Nous avons appris que la fonction hook de React n'est pas si mystérieuse et peut être créée comme les autres fonctions. Nous pouvons créer nos propres hooks spécifiques au domaine et les réutiliser dans toute l'application. Vous pouvez également trouver de nombreux hooks pré-écrits à usage général sur divers blogs ou « bibliothèques de hooks ». Ces hooks peuvent être facilement utilisés dans nos projets, tout comme useState et useEffect. Le hook useInterval de Dan Abramov est un exemple. Par exemple, vous avez un hook similaire à useRepos, mais vous devez pouvoir interroger les mises à jour ? Ensuite, vous pouvez essayer d'utiliser useInterval dans votre hook.

Adresse originale en anglais : https://codescene.com/engineering-blog/refactoring-components-in-react-with-custom-hooks

[Apprentissage recommandé : 🎜Tutoriel vidéo javascript🎜]🎜

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Cet article est reproduit dans:. en cas de violation, veuillez contacter admin@php.cn Supprimer