React Hooks Personnalisés : useCounter

React Hooks Personnalisés : useCounter

·

3 min read

Dans le dernier épisode de la série sur les Hooks personnalisés, nous avons implémenté le Hook useBoolean. Aujourd'hui, nous allons en créer un tout aussi simple et efficace : useCounter. Comme son nom l'indique, il va nous faciliter la manipulation de l'état d'un compteur. Vous êtes prêts ? Allons-y. 🚀

Motivation

Comme toujours, voyons d'abord pourquoi nous voudrions implémenter ce Hook. Imaginons que nous développons un site web d'e-commerce. Lorsqu'un client souhaite acheter un produit sur notre site, il doit également pouvoir sélectionner la quantité. L'interface pourrait donc ressembler à celle ci-dessous (ne faisons pas attention au style).

UI

Et le code source du composant Cart ressemblerait à cela :

const Cart = () => {
  const [quantity, setQuantity] = useState(0);

  return (
    <div className="Cart">
      <h1>My Cart</h1>
      <Item
        label="My awesome item"
        quantity={quantity}
        onIncrement={() => setQuantity((q) => q + 1)}
        onDecrement={() => setQuantity((q) => q - 1)}
        onReset={() => setQuantity(0)}
      />
    </div>
  );
}

Bien sûr, cet exemple est extrêmement simplifié afin de se concentrer sur ce qui nous intéresse le plus dans cet article.

Le composant ci-dessus pourrait être simplifié en se débarrassant de ces horribles fonctions fléchées. De plus, comme nous allons très probablement réutiliser cette logique dans d'autres parties de notre application, nous devrions l'extraire dans un Hook séparé pour éviter la duplication de code. C'est pour cela que nous allons créer le Hook useCounter. Passons maintenant à l'implémentation. 😎

Implémentation

Notre Hook aura donc un état pour contenir la valeur du compteur. Il disposera également de 3 méthodes pour mettre sa valeur à jour : increment, decrement et reset. Dans cette optique, nous pouvons créer notre Hook de la façon suivante :

const useCounter = (initialValue) => {
  const [value, setValue] = useState(initialValue);

  const increment = () => setValue(c => c + 1);
  const decrement = () => setValue(c => c - 1);
  const reset = () => setValue(initialValue);

  return { value, increment, decrement, reset };
};

Certains d'entre vous se demanderont peut-être pourquoi les 3 méthodes exportées ne sont pas encapsulées dans un appel à useCallback, qui permettrait de ne pas les récréer à chaque nouveau rendu du composant. Dans notre cas, le composant est si simple qu'utiliser le Hook useCallback 3 fois pour améliorer les performances pourrait avoir l'effet inverse. Ce Hook devrait plutôt être utilisé si vous savez que le Hook que vous développez sera utilisé dans des composants plus complexes et avec de grandes quantités de données.

Pour plus d'informations, n'hésitez-pas à jeter un œil à cet article de Dmitri Pavlutin, qui explique en détail pourquoi, quand et comment utiliser le Hook useCallback. Cela s'applique également aux Hooks useMemo et useRef.

Ceci étant dit, notre nouveau Hook personnalisé est désormais prêt à être utilisé ! 🥳

Utilisation

Revenons à notre exemple de site web d'e-commerce. Maintenant que nous avons à notre disposition un tout nouveau Hook personnalisé, voici comment le code du composant Cart peut être simplifié :

function Cart() {
  const quantity = useCounter(0);

  return (
    <div className="Cart">
      <h1>My Cart</h1>
      <Item
        label="My awesome item"
        quantity={quantity.value}
        onIncrement={quantity.increment}
        onDecrement={quantity.decrement}
        onReset={quantity.reset}
      />
    </div>
  );
}

Nous nous sommes débarrassés de toutes les fonctions fléchées, ce qui nous donne un code plus propre et plus facile à lire.

Idées d'Améliorations

Pour aller plus loin, voici quelques idées pour améliorer le Hook useCounter. N'hésitez-pas à essayer d'en implémenter une ou plusieurs afin de pratiquer de votre côté.

  • Ajouter un pas d'incrémentation et de décrémentation : counter.increment(step)
  • Définir une valeur minimale et une valeur maximale : useCounter({ min: 0, max: 10, initial: 0 })
  • Définir manuellement la valeur du compteur : counter.set(value)

Conclusion

Avec ce tout nouveau Hook, nous avons désormais une corde de plus à notre arc. Les Hooks que nous avons créés jusqu'à maintenant étaient très simples (certains les considèreront même comme superflus). Dans les prochains articles de cette série, nous commencerons à implémenter des Hooks un peu plus complexes pour réellement nous aider à simplifier nos composants et à éviter la duplication de code.


Code source disponible sur CodeSandbox

Did you find this article valuable?

Support Ludal by becoming a sponsor. Any amount is appreciated!