vendredi 25 septembre 2026

À propos

Contact

Headless & API

Un carrousel ACF Repeater en boucle infinie côté Next.js : le bug de clé React

Un carrousel de témoignages clignotait à chaque revalidation. La cause : un champ Repeater ACF sans identifiant stable, mal transformé en clé React.

Par Clément Hadrot • 25 novembre 2023 • 5 min de lecture • Aucun commentaire
Un carrousel ACF Repeater en boucle infinie côté Next.js : le bug de clé React

Le carrousel de témoignages clignotait. Pas à chaque chargement, non : de façon aléatoire, une fois sur trois environ, les cartes s’intervertissaient une fraction de seconde avant de se stabiliser. Le client d’un projet vitrine pour un réseau d’opticiens l’avait remarqué avant nous, ce qui n’est jamais bon signe pour la crédibilité d’un prestataire.

Le champ en cause était un Repeater ACF nommé temoignages_carrousel, alimenté par le back-office WordPress et consommé par un composant React côté Next.js via l’API REST. Rien d’exotique sur le papier. Et pourtant, c’est exactement ce genre de champ répété qui, mal manipulé côté front, provoque des effets de bord difficiles à reproduire en local.

Le symptôme

Le carrousel affichait bien les bonnes données, mais l’ordre des cartes changeait parfois entre deux rendus consécutifs, sans qu’aucune modification n’ait été faite dans WordPress entre-temps. Le phénomène n’apparaissait qu’en production, jamais en développement local avec le serveur WordPress natif, ce qui a fait perdre un temps précieux à l’équipe : le premier réflexe a été de soupçonner le cache de l’hébergeur front.

En creusant avec l’outil de profilage React, le constat était clair : le composant TemoignageCard se démontait et se remontait entièrement à chaque revalidation, au lieu de simplement mettre à jour ses props. Un signe classique de clé React instable.

Le diagnostic

Le code fautif ressemblait à ceci :

{temoignages.map((item, index) => (
  <TemoignageCard key={index} data={item} />
))}

Utiliser l’index du tableau comme clé est une pratique tolérée uniquement si l’ordre du tableau ne change jamais. Or, le Repeater ACF renvoyé par l’API REST WordPress ne garantit pas un ordre stable des lignes à travers les sauvegarde : un éditeur qui réorganise deux témoignages dans le back-office change la position des objets dans le tableau JSON, sans que leur contenu ne bouge. React, lui, associe la clé 0 au premier objet du tableau, quel que soit son contenu réel. Résultat : au lieu de simplement réordonner deux div existantes dans le DOM, React croit que le contenu du composant à la position 0 a changé et le remonte de zéro, avec un flash visuel le temps que les images se rechargent depuis le cache navigateur.

L'essentiel à retenir : Un id auto-incrémenté côté React ne suffit pas sans clé stable venant d'ACF ; Le tri d'un Repeater côté WordPress change l'ordre des index à chaque sauvegarde ; Une clé dérivée du contenu (hash) règle le problème sans toucher au schéma ACF

Le correctif

La solution la plus propre aurait été d’ajouter un sous-champ ACF de type texte caché, généré automatiquement à la création de chaque ligne, pour servir d’identifiant stable. Mais modifier le schéma ACF impliquait de migrer les données existantes sur un site déjà en production, ce que le client ne voulait pas risquer avant une période de forte affluence commerciale.

La solution retenue a donc été de dériver une clé stable directement à partir du contenu de chaque ligne, côté front, sans toucher à ACF :

function cleStable(item) {
  const base = `${item.nom}-${item.poste}-${item.citation.slice(0, 20)}`;
  return base
    .normalize('NFD')
    .replace(/[̀-ͯ]/g, '')
    .replace(/\s+/g, '-')
    .toLowerCase();
}

{temoignages.map((item) => (
  <TemoignageCard key={cleStable(item)} data={item} />
))}

Cette clé, calculée à partir du nom, du poste et du début de la citation, reste identique quel que soit l’ordre des lignes dans le tableau. React peut alors correctement associer chaque instance de composant à la bonne donnée et se contenter de réordonner les nœuds DOM existants, sans démontage ni flash.

Pourquoi ne pas simplement trier côté WordPress ?

On aurait pu imposer un tri alphabétique ou chronologique systématique du Repeater avant de le renvoyer par l’API, via un filtre register_rest_field personnalisé. Cela aurait réglé le symptôme observé, mais pas la cause : n’importe quel autre champ répété du site restait exposé au même problème dès qu’un éditeur changerait l’ordre des lignes. Corriger la génération de clé côté front est une solution générique, applicable à tous les Repeater du projet, pas seulement à celui du carrousel.

Prévention pour les prochains projets

  • Ajouter systématiquement un sous-champ ACF de type texte, masqué en front-end, qui stocke un identifiant unique généré à la création de chaque ligne d’un Repeater destiné à un front headless.
  • Ne jamais utiliser l’index de tableau comme clé React dès qu’une donnée provient d’un contenu réordonnable par un éditeur non technique.
  • Ajouter un test de non-régression visuel (capture d’écran avant/après réorganisation dans le back-office) sur les composants listes critiques comme les carrousels ou les grilles de témoignages.

Sur ce type de projet, on prend l’habitude de se poser systématiquement la question « qu’est-ce qui identifie cette ligne de façon unique et stable, indépendamment de sa position ? » avant même d’écrire le composant React qui la consomme.

En résumé

Le bug n’était ni dans WordPress, ni dans ACF, ni vraiment dans React : il se situait à la frontière entre les deux, dans l’hypothèse implicite qu’un tableau JSON renvoyé par une API garde un ordre stable dans le temps. Cette hypothèse est presque toujours fausse dès qu’un humain peut modifier le contenu source. La correction a pris moins d’une heure une fois le diagnostic posé, mais le diagnostic lui-même a demandé de sortir des pistes classiques (cache, hydratation, CDN) pour revenir à un principe basique de React trop souvent négligé sur les données dynamiques issues d’un CMS.

Partager :

À propos de l'auteur

Clément Hadrot

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi