Fermer

mai 11, 2022

Un guide pratique pour créer des composants React réutilisables

Un guide pratique pour créer des composants React réutilisables


Bien que React soit l’un des frameworks frontaux les plus populaires et les plus utilisés au monde, de nombreux développeurs ont encore du mal à refactoriser le code pour une meilleure réutilisabilité. Si vous vous êtes déjà retrouvé à répéter le même extrait de code tout au long de votre application React, vous êtes tombé sur le bon article.

Dans ce didacticiel, vous découvrirez les trois indicateurs les plus courants indiquant qu’il est temps de créer un composant React réutilisable. Ensuite, nous examinerons quelques démonstrations pratiques en créant une mise en page réutilisable et deux crochets React passionnants.

Lorsque vous aurez fini de lire, vous serez en mesure de comprendre par vous-même lorsque il est approprié de créer des composants React réutilisables, et comment faire cela.

Cet article suppose une connaissance de base des crochets React et React. Si vous souhaitez approfondir ces sujets, je vous recommande de consulter «Premiers pas avec React« guide et »Introduction à React Hooks”.

Les trois principaux indicateurs d’un composant React réutilisable

Voyons d’abord quelques indices de lorsque vous voudrez peut-être le faire.

Création répétitive de wrappers avec le même style CSS

Mon signe préféré pour savoir quand créer un composant réutilisable est l’utilisation répétitive du même style CSS. Maintenant, vous pensez peut-être : « Attendez une minute : pourquoi n’attribuerais-je pas simplement le même nom de classe aux éléments qui partagent le même style CSS ? Vous avez absolument raison. Ce n’est pas une bonne idée de créer des composants réutilisables chaque fois que certains éléments de différents composants partagent le même style. En fait, cela peut introduire une complexité inutile. Vous devez donc vous demander encore une chose : ces éléments de style courant sont-ils emballages?

Par exemple, considérez les pages de connexion et d’inscription suivantes :


import './common.css';

function Login() {
  return (
    <div className='wrapper'>
      <main>
        {...}
      </main>
      <footer className='footer'>
        {...}
      </footer>
    </div>
  );
}

import './common.css';

function Signup() {
  return (
    <div className='wrapper'>
      <main>
        {...}
      </main>
      <footer className='footer'>
        {...}
      </footer>
    </div>
  );
}

Les mêmes styles sont appliqués au conteneur (le <div> élément) et le pied de page de chaque composant. Donc, dans ce cas, vous pouvez créer deux composants réutilisables — <Wrapper /> et <Footer /> – et leur passer des enfants comme accessoire. Par exemple, le composant de connexion pourrait être refactorisé comme suit :


import Footer from "./Footer.js";

function Login() {
  return (
    <Wrapper main={{...}} footer={<Footer />} />
  );
} 

Par conséquent, vous n’avez plus besoin d’importer common.css dans plusieurs pages ou créer le même <div> éléments pour tout envelopper.

Utilisation répétitive des écouteurs d’événements

Pour attacher un écouteur d’événement à un élément, vous pouvez soit le gérer à l’intérieur useEffect() comme ça:


import { useEffect } from 'react';

function App() {
  const handleKeydown = () => {
    alert('key is pressed.');
  }

  useEffect(() => {
    document.addEventListener('keydown', handleKeydown);
    return () => {
      document.removeEventListener('keydown', handleKeydown);
    }
  }, []);

  return (...);
}

Ou vous pouvez le faire directement dans votre JSX comme ceci, comme illustré dans le composant de bouton suivant :


function Button() {
  return (
    <button type="button" onClick={() => { alert('Hi!')}}>
      Click me!
    </button>
  );
};

Lorsque vous souhaitez ajouter un écouteur d’événement à document ou alors window, vous devez utiliser la première méthode. Cependant, comme vous l’avez peut-être déjà compris, la première méthode nécessite plus de code avec l’utilisation de useEffect(), addEventListener() et removeEventListener(). Ainsi, dans ce cas, la création d’un crochet personnalisé permettra à vos composants d’être plus concis.

Il existe quatre scénarios possibles pour l’utilisation des écouteurs d’événement :

  • même écouteur d’événements, même gestionnaire d’événements
  • même écouteur d’événements, gestionnaire d’événements différent
  • écouteur d’événements différent, même gestionnaire d’événements
  • écouteur d’événement différent, gestionnaire d’événement différent

Dans le premier scénario, vous pouvez créer un hook dans lequel l’écouteur d’événement et le gestionnaire d’événement sont définis. Considérez le crochet suivant :


import { useEffect } from 'react';

export default function useKeydown() {
  const handleKeydown = () => {
    alert('key is pressed.');
  }

  useEffect(() => {
    document.addEventListener('keydown', handleKeydown);
    return () => {
      document.removeEventListener('keydown', handleKeydown);
    }
  }, []);
};

Vous pouvez ensuite utiliser ce crochet dans n’importe quel composant comme suit :


import useKeydown from './useKeydown.js';

function App() {
  useKeydown();
  return (...);
};

Pour les trois autres scénarios, je recommande de créer un hook qui reçoit un événement et une fonction de gestion d’événement en tant qu’accessoires. Par exemple, je passerai keydown et handleKeydown comme accessoires à mon crochet personnalisé. Considérez le crochet suivant :


import { useEffect } from 'react';

export default function useEventListener({ event, handler} ) {
  useEffect(() => {
    document.addEventListener(event, props.handler);
    return () => {
      document.removeEventListener(event, props.handler);
    }
  }, []);
};

Vous pouvez ensuite utiliser ce crochet dans n’importe quel composant comme suit :


import useEventListener from './useEventListener.js';

function App() {
  const handleKeydown = () => {
    alert('key is pressed.');
  }
  useEventListener('keydown', handleKeydown);
  return (...);
};

Utilisation répétitive du même script GraphQL

Vous n’avez pas vraiment besoin de rechercher des signes lorsqu’il s’agit de rendre le code GraphQL réutilisable. Pour les applications complexes, les scripts GraphQL pour une requête ou une mutation occupent facilement 30 à 50 lignes de code car il y a de nombreux attributs à demander. Si vous utilisez le même script GraphQL plus d’une fois ou deux, je pense qu’il mérite son propre crochet personnalisé.

Considérez l’exemple suivant :

import { gql, useQuery } from "@apollo/react-hooks";

const GET_POSTS = gql`
  query getPosts {
    getPosts {
    user {
      id
      name
      ...
      }
      emojis {
         id
         ...
      }
      ...
  }
`;

const { data, loading, error } = useQuery(GET_POSTS, {
  fetchPolicy: "network-only"
});

Au lieu de répéter ce code dans chaque page qui demande des publications depuis le back-end, vous devez créer un crochet React pour cette API particulière :

import { gql, useQuery } from "@apollo/react-hooks";

function useGetPosts() {
  const GET_POSTS = gql`{...}`;
  const { data, loading, error } = useQuery(GET_POSTS, {
    fetchPolicy: "network-only"
  });
  return [data];
}

const Test = () => {
  const [data] = useGetPosts();
  return (
    <div>{data?.map(post => <h1>{post.text}</h1>)}</div>
  );
};

Construire trois composants React réutilisables

Maintenant que nous avons vu des signes courants de lorsque pour créer un nouveau composant que vous pouvez partager dans votre application React, mettons ces connaissances en pratique et construisons trois démos pratiques.

1. Composant de mise en page

React est normalement utilisé pour créer des applications Web complexes. Cela signifie qu’un grand nombre de pages doivent être développées dans React, et je doute que chaque page d’une application ait une mise en page différente. Par exemple, une application Web composée de 30 pages utilise généralement moins de cinq mises en page différentes. Par conséquent, il est essentiel de créer une mise en page flexible et réutilisable pouvant être utilisée dans de nombreuses pages différentes. Cela vous fera économiser de très nombreuses lignes de code et par conséquent un temps considérable.

Considérez le composant fonctionnel React suivant :


import React from "react";
import style from "./Feed.module.css";

export default function Feed() {
  return (
    <div className={style.FeedContainer}>
      <header className={style.FeedHeader}>Header</header>
      <main className={style.FeedMain}>
        {
          <div className={style.ItemList}>
            {itemData.map((item, idx) => (
              <div key={idx} className={style.Item}>
                {item}
              </div>
            ))}
          </div>
        }
      </main>
      <footer className={style.FeedFooter}>Footer</footer>
    </div>
  );
}

const itemData = [1, 2, 3, 4, 5];

Il s’agit d’une page Web typique qui a un <header>un <main> et un <footer>. S’il y a 30 autres pages Web comme celle-ci, vous vous lasserez facilement d’écrire à plusieurs reprises des balises HTML et d’appliquer le même style encore et encore.

Au lieu de cela, vous pouvez créer un composant de mise en page qui reçoit <header>, <main> et <footer> comme accessoires, comme dans le code suivant :


import React from "react";
import style from "./Layout.module.css";
import PropTypes from "prop-types";

export default function Layout({ header, main, footer }) {
  return (
    <div className={style.Container}>
      <header className={style.Header}>{header}</header>
      <main className={style.Main}>{main}</main>
      <footer className={style.Footer}>{footer}</footer>
    </div>
  );
}

Layout.propTypes = {
  main: PropTypes.element.isRequired,
  header: PropTypes.element,
  footer: PropTypes.element
};

Ce composant ne nécessite pas <header> et <footer>. Ainsi, vous pouvez utiliser cette même mise en page pour les pages, qu’elles contiennent un en-tête ou un pied de page.

À l’aide de ce composant de mise en page, vous pouvez transformer votre page de flux en un bloc de code beaucoup plus sophistiqué :


import React from "react";
import Layout from "./Layout";
import style from "./Feed.module.css";

export default function Feed() {
  return (
    <Layout
      header={<div className={style.FeedHeader}>Header</div>}
      main={
        <div className={style.ItemList}>
          {itemData.map((item, idx) => (
            <div key={idx} className={style.Item}>
              {item}
            </div>
          ))}
        </div>
      }
      footer={<div className={style.FeedFooter}>Footer</div>}
    />
  );
}

const itemData = [1, 2, 3, 4, 5];

Conseil de pro pour créer des mises en page avec des éléments collants

De nombreux développeurs ont tendance à utiliser position: fixed ou alors position: absolute lorsqu’ils veulent coller un en-tête en haut de la fenêtre ou un pied de page en bas. Cependant, dans le cas des mises en page, vous devriez essayer d’éviter cela.

Étant donné que les éléments d’une mise en page seront les éléments parents des accessoires passés, vous souhaitez conserver le style de vos éléments de mise en page aussi simple que possible – de sorte que passé <header>, <main>ou alors <footer> sont stylés comme prévu. Je recommande donc de postuler position: fixed et display: flex jusqu’à l’élément le plus extérieur de votre mise en page et de votre cadre overflow-y: scroll au <main> élément.

Voici un exemple :


.Container {
  
  display: flex;
  flex-direction: column;

  
  width: 100%;
  height: 100%;

  
  overflow: hidden;
  position: fixed;
}

.Main {
  
  width: 100%;
  height: 100%;

  
  overflow-y: scroll;
}

Maintenant, appliquons quelques styles à votre page de flux et voyons ce que vous avez créé :


.FeedHeader {
  
  height: 70px;

  
  background-color: teal;
  color: beige;
}

.FeedFooter {
  
  height: 70px;

  
  background-color: beige;
  color: teal;
}

.ItemList {
  
  display: flex;
  flex-direction: column;
}

.Item {
  
  height: 300px;

  
  color: teal;
}

.FeedHeader,
.FeedFooter,
.Item {
  
  display: flex;
  justify-content: center;
  align-items: center;

  
  border: 1px solid teal;

  
  font-size: 35px;
}

Démonstration d’en-tête et de pied de page collants

Et voici le code en action.

Voici à quoi cela ressemble sur les écrans de bureau.

Desktop_Layout

Voici à quoi cela ressemble sur les écrans mobiles.

Mobile_Layout

Cette mise en page fonctionne également comme prévu sur les appareils iOS ! Au cas où vous ne le sauriez pas, iOS est connu pour apporter des problèmes inattendus liés à la position dans le développement d’applications Web.

2. Écouteur d’événement

Souvent, le même écouteur d’événement est utilisé plusieurs fois dans une application Web. Dans un tel cas, c’est une excellente idée de créer un crochet React personnalisé. Apprenons à le faire en développant un useScrollSaver hook, qui enregistre la position de défilement de l’appareil d’un utilisateur sur une page, de sorte que l’utilisateur n’a pas besoin de tout faire défiler à nouveau depuis le haut. Ce crochet sera utile pour une page Web dans laquelle un grand nombre d’éléments, tels que des publications et des commentaires, sont répertoriés ; imaginez les pages de flux de Facebook, Instagram et Twitter sans économiseur de défilement.

Décomposons le code suivant :


import { useEffect } from "react";

export default function useScrollSaver(scrollableDiv, pageUrl) {
  
  const handleScroll = () => {
    sessionStorage.setItem(
      `${pageUrl}-scrollPosition`,
      scrollableDiv.current.scrollTop.toString()
    );
  };
  useEffect(() => {
    if (scrollableDiv.current) {
      const scrollableBody = scrollableDiv.current;
      scrollableBody.addEventListener("scroll", handleScroll);
      return function cleanup() {
        scrollableBody.removeEventListener("scroll", handleScroll);
      };
    }
  }, [scrollableDiv, pageUrl]);

  
  useEffect(() => {
    if (
      scrollableDiv.current &&
      sessionStorage.getItem(`${pageUrl}-scrollPosition`)
    ) {
      const prevScrollPos = Number(
        sessionStorage.getItem(`${pageUrl}-scrollPosition`)
      );
      scrollableDiv.current.scrollTop = prevScrollPos;
    }
  }, [scrollableDiv, pageUrl]);
}

Vous pouvez voir que le useScrollSaver le crochet doit recevoir deux éléments : scrollableDivqui doit être un conteneur déroulant, tout comme le <main> conteneur dans votre mise en page ci-dessus, et pageUrlqui sera utilisé comme identifiant d’une page afin que vous puissiez stocker les positions de défilement de plusieurs pages.

Étape 1 : Enregistrer la position de défilement

Tout d’abord, vous devez lier un écouteur d’événement « scroll » à votre conteneur déroulant :

const scrollableBody = scrollableDiv.current;
scrollableBody.addEventListener("scroll", handleScroll);
return function cleanup() {
  scrollableBody.removeEventListener("scroll", handleScroll);
};

Maintenant, à chaque fois scrollableDiv est fait défiler par un utilisateur, une fonction appelée handleScroll sera exécuté. Dans cette fonction, vous devez utiliser soit localStorage ou alors sessionStorage pour enregistrer la position de défilement. La différence est que les données dans localStorage n’expire pas, alors que les données dans sessionStorage est effacé à la fin de la session de page. Vous pouvez utiliser setItem(id: string, value: string) pour enregistrer des données dans l’un ou l’autre des stockages :

const handleScroll = () => {
  sessionStorage.setItem(
    `${pageUrl}-scrollPosition`,
    scrolledDiv.current.scrollTop.toString()
  );
};

Étape 2 : Restaurer la position de défilement

Lorsqu’un utilisateur revient sur une page Web, l’utilisateur doit être dirigé vers sa position de défilement précédente – s’il y en a une. Ces données de position sont actuellement enregistrées dans sessionStorage, et vous devez le retirer et l’utiliser. Vous pouvez utiliser getItem(id: string) pour obtenir des données du stockage. Ensuite, il vous suffit de définir scroll-top du conteneur déroulant à cette valeur obtenue :

const prevScrollPos = Number(
  sessionStorage.getItem(`${pageUrl}scrollPosition`)
);
scrollableDiv.current.scrollTop = prevScrollPos;

Étape 3 : Utiliser useScrollSaver crochet dans n’importe quelle page Web

Maintenant que vous avez terminé de créer votre hook personnalisé, vous pouvez l’utiliser dans n’importe quelle page Web de votre choix, à condition de lui transmettre les deux éléments requis : scrollableDiv et pageUrl. Revenons à Layout.js et utilisez votre crochet là-dedans. Cela permettra à toute page Web utilisant cette mise en page de profiter de votre économiseur de défilement :


import React, { useRef } from "react";
import style from "./Layout.module.css";
import PropTypes from "prop-types";
import useScrollSaver from "./useScrollSaver";

export default function Layout({ header, main, footer }) {
  const scrollableDiv = useRef(null);
  useScrollSaver(scrollableDiv, window.location.pathname);
  return (
    <div className={style.Container}>
      <header className={style.Header}>{header}</header>
      <main ref={scrollableDiv} className={style.Main}>
        {main}
      </main>
      <footer className={style.Footer}>{footer}</footer>
    </div>
  );
}

Démo Scrollsaver

Et voici le code qui s’exécute dans une Sandbox. Essayez de faire défiler la page, puis utilisez la flèche en bas à gauche et dans le coin pour recharger l’application.

Vous vous retrouverez positionné là où vous vous étiez arrêté !

3. Requête/Mutation (spécifique à GraphQL)

Si vous aimez utiliser GraphQL avec React, comme moi, vous pouvez réduire encore plus votre base de code en créant un crochet React pour les requêtes ou les mutations GraphQL.

Considérez l’exemple suivant pour exécuter une requête GraphQL getPosts():

import { gql, useQuery } from "@apollo/react-hooks";

const GET_POSTS = gql`
  query getPosts {
    getPosts {
    user {
      id
      name
      ...
      }
      emojis {
         id
         ...
      }
      ...
  }
`;

const { data, loading, error } = useQuery(GET_POSTS, {
  fetchPolicy: "network-only"
});

S’il y a de plus en plus d’attributs à demander au back-end, votre script GraphQL prendra de plus en plus de place. Ainsi, au lieu de répéter le script GraphQL et useQuery chaque fois que vous devez exécuter une requête getPosts()vous pouvez créer le crochet React suivant :


import { gql, useQuery } from "@apollo/react-hooks";

export default function useGetPosts() {
  const GET_POSTS = gql`
  query getPosts {
    getPosts {
    user {
      id
      name
      ...
      }
      emojis {
         id
         ...
      }
      ...
  }
  `;

  const { data, loading, error } = useQuery(GET_POSTS, {
    fetchPolicy: "network-only"
  });

  return [data, loading, error];
}

Ensuite, vous pouvez utiliser votre useGetPosts() crochet comme suit :


import React from "react";
import Layout from "./Layout";
import style from "./Feed.module.css";
import useGetPosts from "./useGetPosts.js";

export default function Feed() {
  const [data, loading, error] = useGetPosts();
  return (
    <Layout
      header={<div className={style.FeedHeader}>Header</div>}
      main={
        <div className={style.ItemList}>
          {data?.getPosts.map((item, idx) => (
            <div key={idx} className={style.Item}>
              {item}
            </div>
          ))}
        </div>
      }
      footer={<div className={style.FeedFooter}>Footer</div>}
    />
  );
}

Conclusion

Dans cet article, vous avez appris les trois indicateurs les plus courants d’un composant React réutilisable et les trois cas d’utilisation les plus populaires. Maintenant, vous avez la connaissance de lorsque pour créer un composant React réutilisable et comment le faire facilement et professionnellement. Vous vous retrouverez bientôt à profiter de la refactorisation de lignes de code dans un composant ou un crochet React sophistiqué et réutilisable. En utilisant ces techniques de refactoring, notre équipe de développement à Argile a pu réduire notre base de code à une taille gérable. J’espère que tu peux aussi!




Source link