# React Hooks Personnalisés : useCounter

Dans le dernier épisode de la série sur les Hooks personnalisés, nous avons implémenté le Hook
[useBoolean](https://blog.iamludal.fr/react-hooks-personnalises-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](https://cdn.hashnode.com/res/hashnode/image/upload/v1633869919339/RLNAmwg8y.png)

Et le code source du composant `Cart` ressemblerait à cela :

```jsx
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 :

```jsx
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](https://dmitripavlutin.com/dont-overuse-react-usecallback)
> de [Dmitri Pavlutin](https://dmitripavlutin.com/), 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é :

```jsx
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](https://codesandbox.io/s/custom-react-hooks-usecounter-5w9gk?file=/src/hooks/useCounter.ts)

