Le hook useSyncExternalStore

Résumé Support

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.