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.