Le Hook le plus sous-estimé de React : useSyncExternalStore
ReactIntroduction
Bienvenue sur ce post où nous allons explorer un hook React méconnu mais extrêmement utile : useSyncExternalStore. Ce hook peut grandement faciliter la synchronisation de vos données provenant de sources externes, comme le localStorage ou les API du navigateur.
Nous commencerons par la méthode classique avec useEffect, puis nous verrons comment optimiser notre code avec useSyncExternalStore. Enfin, nous terminerons par un exemple pratique avec l’API navigator.onLine
pour suivre l’état de connexion de l’utilisateur.
Synchronisation avec useEffect
Imaginons que nous voulons synchroniser une valeur provenant du localStorage. La première idée qui vient à l’esprit est d’utiliser useEffect pour surveiller les changements.
Voici comment pourrait ressembler notre composant :
"use client";
import { faker } from "@faker-js/faker";
import { useEffect, useState } from "react";
import { Button } from "./ui/button";
const EMAIL_KEY = "email";
export default function LocalStorageDemo() {
const [value, setValue] = useState(
() => localStorage.getItem(EMAIL_KEY) ?? ""
);
useEffect(() => {
const handleStorageChange = () => {
setValue(localStorage.getItem(EMAIL_KEY) as string);
};
window.addEventListener("storage", handleStorageChange);
return () => {
window.removeEventListener("storage", handleStorageChange);
};
}, []);
function updateEmailHandler() {
const newEmail = faker.internet.email();
localStorage.setItem(EMAIL_KEY, newEmail);
window.dispatchEvent(new Event("storage"));
}
return (
<div className="space-y-6 mt-8">
<p>Adresse email : {value}</p>
<Button onClick={updateEmailHandler}>Changer l'email</Button>
</div>
);
}
Voici l’explication du code ci-dessus :
useState
: On initialise l’état value avec la valeur du localStorage pour la clé email.useEffect
: On ajoute un écouteur d’événements pour détecter les changements sur le localStorage. La fonctionhandleStorageChange
met à jour l’état du composant.updateEmailHandler
: Cette fonction génère un nouvel email et le stocke dans le localStorage, puis déclenche l’événement storage pour notifier le changement.
Bien que cette approche fonctionne, elle peut devenir complexe à gérer, surtout si vous avez plusieurs sources de données externes à synchroniser.
Optimisation avec useSyncExternalStore
Le hook useSyncExternalStore est conçu pour synchroniser de manière efficace des états externes dans vos composants React.
Voici comment refactorer le composant précédent en utilisant useSyncExternalStore :
"use client";
import { faker } from "@faker-js/faker";
import { useSyncExternalStore } from "react";
import { Button } from "./ui/button";
const EMAIL_KEY = "email";
const DEFAULT_VALUE = "";
function subscribe(callback: () => void) {
window.addEventListener("storage", callback);
return () => window.removeEventListener("storage", callback);
}
export default function LocalStorageDemo() {
const value = useSyncExternalStore(
subscribe,
() => localStorage.getItem(EMAIL_KEY) ?? DEFAULT_VALUE,
() => DEFAULT_VALUE // Pour le Server-Side Rendering (SSR)
);
function updateEmailHandler() {
const newEmail = faker.internet.email();
localStorage.setItem(EMAIL_KEY, newEmail);
window.dispatchEvent(new Event("storage"));
}
return (
<div className="space-y-6 mt-8">
<p>Adresse email : {value}</p>
<Button onClick={updateEmailHandler}>Changer l'email</Button>
</div>
);
}
Voici l’explication du code ci-dessus :
subscribe
: Une fonction qui souscrit aux changements de l’état externe. Elle est définie en dehors du composant pour conserver la même référence entre les rendus.useSyncExternalStore
: Ce hook prend en paramètres la fonction subscribe, une fonction de lecture de l’état actuel, et une fonction pour l’état initial lors du Server-Side Rendering.updateEmailHandler
: Identique à la version précédente.
Voici les avantages :
- Simplification du code : Moins de logique liée à la synchronisation de l’état dans votre composant.
- Performance : useSyncExternalStore est optimisé pour minimiser les re-rendus inutiles.
- Lisibilité : Une approche plus déclarative et facile à comprendre.
Exemple avec l’API navigator.onLine
Pour illustrer davantage l’utilité de useSyncExternalStore, prenons un autre exemple : suivre l’état de connexion de l’utilisateur.
import React, { useSyncExternalStore } from "react";
function subscribe(callback: () => void) {
window.addEventListener("online", callback);
window.addEventListener("offline", callback);
return () => {
window.removeEventListener("online", callback);
window.removeEventListener("offline", callback);
};
}
export default function NetworkStatus() {
const isOnline = useSyncExternalStore(subscribe, () => navigator.onLine);
return (
<div>
<h1>{isOnline ? "Vous êtes en ligne" : "Vous êtes hors ligne"}</h1>
</div>
);
}
Voici l’explication du code ci-dessus :
subscribe
: On écoute les événements online et offline du navigateur.useSyncExternalStore
: Synchronise l’état isOnline avec l’état de connexion du navigateur.
Conclusion
Le hook useSyncExternalStore est un outil puissant pour synchroniser des données externes avec vos composants React de manière efficace et propre. Il permet de simplifier votre code tout en améliorant les performances de votre application.
Points Clés
- Optimisation : Moins de re-rendus et une gestion plus efficace de l’état.
- Simplicité : Une API claire qui facilite la synchronisation des états externes.
- Flexibilité : Applicable à diverses sources de données externes comme le localStorage, les APIs du navigateur, etc.
Ressources supplémentaires
- Code source du projet : https://github.com/codingtechacademy/use-sync-external-store-demo