React Hooks Personnalisés : useAudio

React Hooks Personnalisés : useAudio

·

4 min read

Après avoir découvert le Hook useNetworkState pour simplifier la gestion de l'état du réseau de l'utilisateur, implémentons un nouveau Hook personnalisé : useAudio. Vous êtes prêts ? C'est parti. 😎

Motivation

Tout d'abord, pourquoi aurions-nous besoin de ce Hook ? Je vais vous donner deux exemples.

Le premier est celui de mon site web personnel, iamludal.fr (non, ce n'est pas de l'autopromotion 🙄), développé en React, et dont la barre de navigation contient un bouton permettant de changer de thème (clair ou sombre). En fait, lorsque l'on clique sur ce dernier, on peut entendre le son d'un interrupteur se déclencher. Ce son provient de ce Hook personnalisé.

Le second exemple est celui du jeu Typospeed (pas d'autopromotion non-plus, je vous assure), dans lequel un son de bulle qui éclate se fait entendre lorsque l'on tape correctement un mot. À vrai dire, Typospeed a été développé en Svelte, mais vous voyez où je veux en venir.

Dans ces exemples, nous avons besoin de jouer des effets sonores pour donner plus de vie à notre application. Mais nous ne voulons pas nous répéter : créer manuellement une instance de la classe Audio, définir son volume, sa vitesse...

const Home = () => {
  const audio = useRef(new Audio('/switch.mp3'));

  useEffect(() => {
    audio.current.playbackRate = 1.5;
    audio.current.volume = 0.8;
  }, []);

  return <button onClick={audio.current.play}>Play Sound</button>;
};

Nous ne voulons pas écrire tout ça à chaque fois que l'on a besoin d'utiliser des effets sonores. De plus, nous devons explicitement utiliser le Hook useRef et sa valeur courante pour éviter que l'instance de la classe Audio ne soit recréée à chaque rendu du composant.

Ceci étant dit, nous avons maintenant des raisons suffisantes pour implémenter ce Hook personnalisé. Mettons les mains dans le cambouis ! 👨🏻‍💻

Implémentation

Comme nous venons de le voir, notre but premier est de ne pas nous répéter (et c'est le but principal des Hooks personnalisés). De ce fait, notre fonction pourra prendre des paramètres optionnels, qui pourront être statiques (des constantes) ou dynamiques (variables issues d'autres Hooks, tels que useState). Ces paramètres correspondent aux options de l'instance de la classe Audio.

const audio = useAudio('/switch.mp3', { volume: 0.8 });

Mais ce n'est pas tout : nous voulons également nous débarrasser de l'attribut .current. Cette logique doit-être extraite au sein du Hook. De ce fait, nous pourrons interagir directement avec l'instance.

audio.play();
audio.pause();

Le squelette ressemblera donc à ceci :

const useAudio = src => {
  const audio = useRef(new Audio(src));

  return audio.current;
};

C'est une première version, basique, de notre Hook. Si vous n'avez pas besoin d'avoir des options supplémentaires, votre aventure peut s'arrêter ici. Mais nous allons quand même lui ajouter un autre paramètre : un objet pour les options. Nous ferons attention à ce que le changement de chacune des propriétés de cet objet reçu en paramètre mette à jour l'instance de la classe Audio. Ainsi, la vitesse et le volume pourront être mises à jour dynamiquement depuis l'extérieur (par exemple via un autre Hook, comme useState).

Voici donc l'implémentation finale de notre Hook personnalisé :

const useAudio = (src, { volume = 1, playbackRate = 1 }) => {
  const audio = useRef(new Audio(src));

  useEffect(() => {
    audio.current.volume = volume;
  }, [volume]);

  useEffect(() => {
    audio.current.playbackRate = playbackRate;
  }, [playbackRate]);

  return audio.current;
};

Si vous avez besoin d'autres options, n'hésitez-pas à les ajouter en fonction de vos besoins. Par exemple, vous pourriez ajouter un nouveau paramètre optionnel à la méthode play : une liste à deux éléments, le temps de début et de fin, permettant de ne jouer qu'une partie du fichier audio. Cela peut être particulièrement utile lorsque vous avez un fichier audio qui contient plusieurs effets sonores à la suite (c'est une technique que l'on retrouve dans certains jeux).

Notre Hook ne demande maintenant plus qu'à être utilisé. 🤘

Utilisation

Si l'on revient à notre exemple initial, le code peut désormais être simplifié de la façon suivante :

const Home = () => {
  const audio = useAudio('/switch.mp3', { volume: 0.8, playbackRate: 1.5 });

  return <button onClick={audio.play}>Play Sound</button>;
};

Nous avons réussi à abstraire toute la logique de départ dans un Hook personnalisé, ce qui aboutit à un code plus simple, plus propre et plus lisible. De plus, il peut être réutilisé n'importe où dans notre application, et ce, sans complexité additionnelle.


Code source disponible sur CodeSandbox.

Did you find this article valuable?

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