Sur un site vitrine pour une école de formation, deux blocs devaient dialoguer sans être parents et enfants l’un de l’autre : un bloc « sélecteur de formule » posé n’importe où dans la page, et un bloc « résumé flottant » affiché en bas d’écran, qui devait refléter la formule choisie. Le contexte de bloc classique (providesContext / usesContext) ne convient pas ici, car les deux blocs ne partagent pas d’ancêtre commun garanti.
La solution passe par un store de données personnalisé et un abonnement ciblé via wp.data.subscribe. Mais mal utilisé, cet abonnement peut déclencher une cascade de rerenders sur tout l’éditeur, car il s’exécute à chaque changement d’état, quel qu’il soit.
Le store partagé entre les deux blocs
Le store, déclaré avec createReduxStore et enregistré via register(), expose une formule sélectionnée et une action pour la modifier. Ce mécanisme est déjà largement documenté ailleurs ; ce qui nous intéresse ici, c’est l’abonnement en lecture depuis un bloc qui n’est ni l’auteur ni le consommateur direct du changement.
L’erreur initiale : un abonnement global qui rerender tout
La première version du bloc « résumé flottant » utilisait wp.data.subscribe sans filtrer la source du changement :
useEffect(() => {
const desabonner = wp.data.subscribe(() => {
setFormule(select('ecole/formules').getFormuleActive());
});
return desabonner;
}, []);

Le callback passé à subscribe s’exécute à chaque mutation de n’importe quel store, y compris core/block-editor qui change en permanence pendant la frappe. Résultat : à chaque caractère tapé dans un autre bloc de la page, setFormule était appelé, provoquant un rerender du bloc résumé même si la formule active n’avait pas bougé.
Le correctif : comparer avant de notifier
La solution tient en une comparaison de valeur avant d’appeler le setter React, pour que le composant ne se rerende que si la donnée qui l’intéresse a réellement changé :
useEffect(() => {
let derniereFormule = select('ecole/formules').getFormuleActive();
const desabonner = wp.data.subscribe(() => {
const formuleCourante = select('ecole/formules').getFormuleActive();
if (formuleCourante !== derniereFormule) {
derniereFormule = formuleCourante;
setFormule(formuleCourante);
}
});
return desabonner;
}, []);
Le callback continue de s’exécuter à chaque mutation de store (c’est le fonctionnement normal de subscribe), mais il ne déclenche un rerender React que lorsque la valeur surveillée a effectivement changé. C’est le même principe que useSelect applique en interne avec sa mémoïsation, mais ici écrit à la main puisque useSelect seul ne suffit pas pour ce cas de communication inter-blocs sans hiérarchie.
Pourquoi ne pas simplement utiliser useSelect ici
useSelect aurait fonctionné, et reste la solution à privilégier dans la majorité des cas. Nous l’avons écarté ici pour une raison précise du projet : le bloc résumé n’est pas systématiquement remonté par React à chaque mutation de son propre état, car il est rendu dans un Portal hors de l’arbre habituel de l’éditeur, avec un cycle de montage particulier. Un abonnement manuel via subscribe, désabonné explicitement au démontage, donnait un contrôle plus fin sur ce cycle.
Toujours se désabonner
Un oubli fréquent : ne pas retourner la fonction de désabonnement dans useEffect. Chaque bloc résumé instancié (par exemple lors d’un aperçu ou d’une réutilisation du composant dans un autre contexte) laisse alors un abonnement actif indéfiniment, qui continue de s’exécuter même après démontage du composant. Sur une page avec plusieurs dizaines de blocs, ces abonnements fantômes finissent par ralentir sensiblement l’éditeur.
- Toujours retourner la fonction renvoyée par
subscribe()dans le nettoyage deuseEffect. - Comparer la valeur surveillée avant d’appeler un setter React.
- Réserver
subscribeaux cas oùuseSelectne convient pas, et documenter pourquoi dans le code.
Un abonnement qui ne compare rien n’est pas un abonnement, c’est une boucle d’exécution déguisée.
Notre verdict
wp.data.subscribe reste un outil légitime pour faire communiquer deux blocs sans lien de parenté, mais il exige une discipline que useSelect impose par défaut : comparer la valeur avant de notifier React, et se désabonner proprement. Sur ce projet, ce correctif a fait passer le nombre de rerenders du bloc résumé pendant une frappe de plusieurs dizaines à exactement un, au moment où la formule change réellement.