Le hook useSyncExternalStore — Formation Apprendre React

Dans ce tutoriel je vous propose de découvrir un hook un peu particulier : useSyncExternalStore. Ce hook va permettre de brancher un store externe à React et de déclencher un nouveau rendu lors d’un changement de valeur.

Principe général

Le principe général de ce hook est de venir s’abonner à un système de stockage externe pour les intégrer à React à l’aide d’un subscriber. Il attend 3 paramètres :

  • Une méthode d’abonnement qui recevra en paramètre un callback qu’il faudra appelé lorsque la valeur dans le store change.
  • Une méthode permettant de récupérer la valeur à l’intérieur du store.
  • Une méthode optionnelle pour récupérer la valeur dans le cas d’un rendu côté serveur.

Pour vous donner un exemple, voici comment on pourrait utiliser ce hook pour se connecter à l’API du navigateur pour détecter l’état du réseau.

// Méthode permettant de s'abonner aux changements
function subscribe(callback) {
  window.addEventListener('online', callback);
  window.addEventListener('offline', callback);

  // On retourne une fonctionne pour se désabonner
  return () => {
    window.removeEventListener('online', callback);
    window.removeEventListener('offline', callback);
  };
}

// Méthode pour récupérer la valeur
function getSnapshot() {
  return navigator.onLine;
}

// On peut ensuite combiner avec useSyncExternalStore
export function useIsOnline () {
    return useSyncExternalStore(subscribe, getSnapshot, getSnapshot)
}

On fera attention à ne pas passer une fonction d’abonnement qui change à chaque rendu sinon React se désabonnera et re-abonnera à chaque re-rendu.

Utilisation des signals

Dans une vidéo précédente on a vu le principe des “signal” qui permettent de gérer facilement des données réactives.

// Avec @maverick-js/signals
import { useCallback, useSyncExternalStore } from "react";
import { effect } from "@maverick-js/signals";

function useSignalValue(signal) {
  const subscribe = useCallback(
    (onChange) => effect(() => onChange(signal())),
    []
  );
  return useSyncExternalStore(subscribe, signal, signal);
}

Ce qui permet ensuite de créer un état global partagé entre plusieurs composants

import { signal } from "@maverick-js/signals";

const $count = signal(0)
export const increment = () => $count.set($count() + 1)
export const useCount = () => useSignalValue($count)

Le gros avantage est qu’il est possible d’intégrer n’importe quel store à React au travers de ce hook.