Vingt questions d’interview React pour distinguer l’utilisation de la compréhension
DOM virtuel, clés, effets, mémorisation, Context API, SSR et hydratation : explications basées sur les questions pièges auxquelles les interviewers se réfèrent vraiment, et non sur des définitions tirées de manuels scolaires.
Maîtriser React et en expliquer les principes sont deux compétences distinctes. Les entretiens portent sur cette seconde : ce qui se passe lors de l’appel à setState, pourquoi les clés sont importantes, et quand les effets se réexécutent. Les vingt questions ci-dessous reviennent constamment. Les réponses s’appuient sur le problème que résout chaque fonctionnalité ainsi que sur les pièges qui peuvent survenir en production, et non sur des récitations de manuels.
1. Ce qu’est réellement le Virtual DOM
Le Virtual DOM n’est pas de la poudre magique d’accélération mystérieuse. C’est simplement un arbre JavaScript qui décrit à quoi doit ressembler l’interface utilisateur. Lorsqu’un état change, React crée un nouvel arbre virtuel, le compare avec l’ancien et effectue des mises à jour en appliquant uniquement les modifications nécessaires au DOM du navigateur. Les opérations sur le DOM réel déclenchent des ajustements de mise en page et du rendu ; modifier des objets JavaScript est peu coûteux, c’est pourquoi React utilise la puissance de traitement pour travailler en mémoire et ainsi limiter les charges sur le navigateur. Cette conception vise à minimiser les surcharges du navigateur, et non à rendre chaque comparaison gratuite. Affirmer que « le Virtual DOM est toujours plus rapide » sans cette nuance est une erreur fréquente chez les débutants.
2. Virtual DOM versus le DOM du navigateur
Les mises à jour du DOM réel sont coûteuses et peuvent entraîner un reflow ainsi qu’un repainting. Les mises à jour virtuelles consistent en des différences d’objets en mémoire ; React regroupe les écritures coûteuses sur le DOM plutôt que d’effectuer une opération par appel de fonction d’état. Utiliser track-changes pour modifier le contenu est préférable à la réécriture complète du document à chaque faute d’orthographe.
3. Pourquoi les clés de liste sont importantes lorsque les lignes changent de position
Les clés permettent d’identifier les éléments entre différentes rendus. Sans elles, React doit se référer à la position dans l’index, ce qui ne fonctionne plus lorsque des éléments sont insérés, supprimés ou réordonnés. Utiliser l’index de tableau comme clé « fonctionne » tant que le réordonnement ne fait pas correspondre des champs de saisie ou des cases à cocher aux lignes incorrectes — ce qui constitue une fausse « fuite d’état » due en réalité à un problème de clé. Préférez des identifiants uniques et stables provenant des données ; utilisez uniquement les indices pour des listes véritablement statiques. Si le produit permet de réordonner ou de filtrer par glisser-déposer, les indices utilisés comme clés finiront par corrompre l’état local des lignes.
4. Expliquer useState à partir des principes fondamentaux
Les éléments locaux ordinaires sont détruits lorsque une fonction retourne. useState fournit à un composant fonctionnel une mémoire persistante et planifie un re-render lorsque cette mémoire change :
const [count, setCount] = useState(0);
count représente la valeur actuelle ; setCount demande une mise à jour. La mise à jour n’est pas appliquée au cours du re-render : en affichant count immédiatement après setCount, on voit toujours la valeur précédente, car la nouvelle valeur apparaît lors du prochain re-render.
5. Pourquoi les mises à jour de useState semblent différées ou regroupées
React regroupe les mises à jour qui surviennent lors du même événement en un seul re-render. Depuis React 18, ce regroupement s’applique également aux promesses et aux timers, et non seulement aux gestionnaires d’événements de React. Lorsque vous avez besoin de la valeur la plus récente en fonction de l’état précédent, utilisez l’updater fonctionnel :
setCount(prev => prev + 1);
Ce formulaire lit la valeur la plus récente de la file d’attente plutôt qu’une capture obsolète du contexte. Les interviewers demandent souvent ce qui se passe si l’on ferme le référentiel sur count à l’intérieur d’un délai imparti sans le formulaire de mise à jour.
6. Quel problème useEffect résout-il réellement ?
Le rendu devrait être une fonction pure qui transforme les props et l’état en JSX. Les applications chargent également des données, attachent des écouteurs d’événements, planifient des temporiseurs et interagissent avec le DOM — ce sont des effets secondaires. useEffect exécute ces tâches impures après l’exécution du rendu, et non pendant celui-ci :
useEffect(() => {
const id = setInterval(() => console.log("tick"), 1000);
return () => clearInterval(id); // cleanup
}, []);
La fonction de nettoyage retournée s’exécute avant le prochain effet et lors du démontage. En l’omettant, les interviewers poseront des questions sur des temporiseurs dupliqués et des écouteurs qui continuent de fonctionner après le démontage.
7. Qu’est-ce que l’array de dépendances contrôle réellement ?
Il indique à React quand relancer l’effet grâce à des comparaisons superficielles :
[]— une fois après le montage[count]— à nouveau lorsquecountchange- omis — après chaque rendu (rarement souhaité)
Oubliez une valeur référencée dans le tableau et vous obtiendrez une fermeture obsolète : l’effet conserve à jamais la première valeur capturée. Des règles de vérification exhaustives existent car ce type de bug est très courant.
8. Composants contrôlés vs non contrôlés
Les entrées contrôlées prennent la valeur value de l’état React et s’actualisent via onChange ; React détient la vérité.
Les entrées non contrôlées conservent l’état du DOM ; elles sont lues via ref lorsque c’est nécessaire (souvent lors de la soumission).
// Controlled
<input value={name} onChange={e => setName(e.target.value)} />
// Uncontrolled
<input ref={inputRef} defaultValue="Dev" />
Le mode contrôlé active la validation en temps réel et le formatage, mais au prix d’un rendu à chaque touche. Le mode non contrôlé reste plus léger lorsque seuls la valeur finale sont nécessaires. Les formulaires mixtes — certains champs contrôlés, d’autres non — sont possibles, mais plus difficiles à gérer lors des revues.
9. Le transfert de propriétés et le moment d’arrêter
Le transfert de propriétés transmet les données à travers des couches qui se contentent de les rediriger. Les renommages affectent de nombreux fichiers. Le contexte est utile pour les thèmes, l’authentification ou la localisation ; Redux ou Zustand aident lorsque les graphes d’état deviennent complexes. Nuance : le contexte n’est pas gratuit — chaque consommateur se rérendu à nouveau lorsque la valeur change — donc il n’est pas la solution par défaut pour tous les champs partagés. Transmettre des propriétés à deux niveaux est souvent plus clair que d’inventer un fournisseur de contexte pour une valeur ponctuelle.
10. Dans quels cas choisir useReducer plutôt que useState ?
Utilisez useReducer lorsque les mises à jour se divisent selon le type d’action, que l’état suivant dépend de l’état précédent de manières complexes, ou lorsque plusieurs champs évoluent ensemble :
function reducer(state, action) {
switch (action.type) {
case "increment": return { count: state.count + 1 };
case "reset": return { count: 0 };
default: return state;
}
}
const [state, dispatch] = useReducer(reducer, { count: 0 });
C’est un mini-Redux à l’intérieur du composant. Un interrupteur booléen reste géré par useState ; une logique de transition complexe doit être placée dans un réducteur testable. En extrayant cette logique des gestionnaires d’événements JSX, les tests unitaires deviennent simples sans avoir à rendre toute la structure.
11. Expliquez useMemo vs useCallback sans vous contenter de citer la documentation
Tous deux évitent les travaux redondants entre les rendus pour différents types de données :
useMemocache une valeur calculéeuseCallbackcache une référence de fonction
const sorted = useMemo(() => expensiveSort(list), [list]);
const handleClick = useCallback(() => doThing(id), [id]);
L’identité de la fonction est importante car chaque rendu crée un nouvel objet fonction. Fournir une fonction neuve à un enfant memo annule cette mise en mémoire ; useCallback maintient la référence stable. Ne mettez pas ces hooks partout — la mise en mémoire a un coût. Utilisez-les pour des tâches coûteuses à effectuer avec prudence ou pour des enfants mis en mémoire. Une mise en mémoire prématurée est une erreur fréquente chez les juniors que les interviewers aiment souligner.
12. React.memo est utile — et facile à contourner
React.memo évite un nouveau rendu lorsque les props sont strictement égaux. De nouveaux littéraux d’objet ou d’array sont considérés comme différents même s’ils contiennent les mêmes données, de sorte que { style: { color: 'red' } } rend memo inutile à moins que les parents ne stabilisent les props avec useMemo/useCallback. Sinon, memo ajoute un coût de comparaison sans empêcher l’exécution des tâches de l’enfant.
13. Les clés en tant qu’identité, et non seulement un avertissement de console
Les clés constituent le système d’identité de React entre les rendus. Des clés incorrectes réutilisent le mauvais nœud DOM pour les mauvaises données : l’état du formulaire reste lié à une autre ligne, les animations s’exécutent sur le mauvais élément, et useState des éléments de liste conserve la valeur de l’élément précédent. Cela ressemble à une corruption d’état ; il s’agit en réalité d’un bug lié aux clés. Montrer une liste avec un index-key défectueux dans un environnement de test est l’un des moyens les plus rapides pour assimiler cette règle.
14. Le contexte : les bonnes utilisations et les mauvaises
Le contexte partage des valeurs nécessaires à de nombreux composants sans avoir recours au transfert de propriétés — utilisateur authentifié, thème, locale :
const ThemeContext = createContext();
<ThemeContext.Provider value={theme}>
<App />
</ThemeContext.Provider>
Cela ne convient pas bien aux états à haute fréquence (valeurs de formulaire par touche) dans un arbre complexe, car chaque consommateur est mis à jour à chaque modification sans abonnement sélectif. Préférez un stockage avec des abonnements plus fins pour ce type de structure. Les interrupteurs de thème constituent un exemple classique d’adaptation contextuelle efficace ; en revanche, les positions du curseur dans un éditeur collaboratif ne le sont généralement pas.
15. Mappage des cycles de vie des classes sur les effets
Mappage approximatif des classes :
componentDidMount→useEffect(..., [])componentDidUpdate→useEffect(..., [dep])componentWillUnmount→ nettoyage des effets
Le changement fondamental : les cycles de vie fonctionnent en termes de temps (chargement/mise à jour/déchargement) ; les effets, quant à eux, reposent sur la synchronisation — il s’agit de maintenir ce système externe en accord avec ces valeurs — d’où la nécessité de relancer les effets lorsque les dépendances changent. Traiter les effets comme des méthodes de cycle de vie traduites ligne par ligne explique pourquoi on se retrouve à lutter contre le tableau des dépendances.
16. État comparé aux props
Les props sont des entrées en lecture seule provenant d’un composant parent. L’état, lui, est des données propriétaires qui déclenchent un nouveau rendu lorsqu’elles changent. Les props configurent un composant depuis l’extérieur ; l’état, c’est ce que le composant se souvient de lui-même. Le label d’un bouton est un prop ; le fait qu’il soit désactivé pendant qu’une requête est en cours est de l’état. Confondre les deux entraîne des anti-modèles tels que tenter de modifier des props ou d’éléver trop haut des indicateurs UI éphémères.
17. Identifier les rendus inutiles sans deviner
Causes fréquentes : les parents qui transmettent des littéraux d’objet/array/fonction, le re-rendering de tous les consommateurs en raison d’un changement de contexte, ou un état placé à un niveau trop élevé. Ne tentez pas de deviner — utilisez le Profilleur des outils de développement React, enregistrez une interaction et voyez quels props ont changé. Les correctifs consistent souvent à descendre l’état ou à diviser les composants afin que des sous-arbres coûteux cessent d’être mis à jour avec des mises à jour peu coûteuses, plutôt que d’utiliser d’abord useMemo. Les profilleurs transforment le sentiment de lenteur en une explication concrète concernant les parents et les props que vous pouvez corriger.
18. SSR comparé à CSR
CSR envoie un noyau HTML léger ainsi que du JS ; le navigateur construit la page après téléchargement — rapide à servir, mais plus lent pour l’affichage réel, et historiquement moins performant en SEO tant que le JS n’a pas été exécuté. SSR envoie de l’HTML pour chaque requête, puis hydrate la page en ajoutant des écouteurs — ce qui améliore l’affichage initial et les performances SEO, mais augmente la charge serveur. Next.js et d’autres outils ajoutent la génération statique et le streaming, mais l’équilibre fondamental reste entre le temps de réponse/SEO d’un côté, et le coût et la complexité du serveur de l’autre. Affirmer que « SSR est toujours meilleur » sans mentionner cet équilibre constitue une réponse faible lors d’un entretien.
19. Incohérences liées à l’hydratation et leurs causes
L’hydratation relie React au HTML du serveur sans jeter le markup. Des incohérences surviennent lorsque l’HTML du serveur diffère de la première rendu côté client : utilisation de Date.now() ou Math.random() lors du rendu, des vérifications sur window qui diffèrent sur le serveur, ou l’injection de nœuds par des extensions. React émet des avertissements sonores et rérendu fréquemment côté client pour se remettre en état — ce qui entraîne un travail supplémentaire ainsi qu’une apparition visible de contenu incorrect. La méthode préventive habituelle consiste à protéger les API réservées au navigateur derrière useEffect ou des composants fonctionnels.
20. Pourquoi React enrobe les événements natifs dans SyntheticEvent
Le SyntheticEvent de React normalise les particularités entre navigateurs (afin que onChange se comporte de manière cohérente) et utilisait historiquement la délégation d’événements au niveau racine plutôt qu’un écouteur natif par nœud. React 17+ délègue les événements au conteneur racine de l’application plutôt qu’à document, mais le principe reste identique. Auparavant, on recourait à un pool d’objets d’événements pour les réutiliser, ce qui pouvait entraîner la valeur null lors d’accès asynchrone ; ce système a disparu depuis React 17, mais la connaissance de l’étage intermédiaire entre l’événement du navigateur et le gestionnaire indique toujours le niveau de profondeur. Le fait de mentionner que e.nativeEvent existe toujours en dessous montre que l’on comprend bien que cette abstraction n’est qu’un enveloppement, et non un remplacement du modèle d’événements DOM.
Ce que les entretiens récompensent vraiment
De bonnes réponses lors d’un entretien expliquent le problème, les difficultés rencontrées et la manière dont vous résoudriez les problèmes d’un collègue — et non une définition mémorisée. Les interviewers s’intéressent moins à votre capacité à définir useEffect qu’au fait de savoir si des fermetures obsolètes, un manque d’opérations de nettoyage ou des erreurs de dépendance ont eu un impact négatif sur vous, et si vous pouvez en expliquer les raisons. Reproduisez ces bugs dans un environnement de test avant l’entretien ; des expériences concrètes témoignent d’une véritable compréhension. Les définitions vous aident à passer la première minute ; les compromis et les histoires d’échecs animent le reste de la conversation.
Gardez un bref résumé personnel des bugs que vous avez corrigés — effets obsolètes, touches défectueuses, incohérences de hydratation — et entraînez-vous à en expliquer chaque un en moins d’une minute. Cette préparation vaut mieux que de réviser frénétiquement les signatures API la veille au soir, et elle vous permet de fournir des exemples concrets lorsque l’intervieweur demande un cas pratique issu de votre travail. Associez chaque exemple à la correction que vous avez mise en œuvre afin que votre réponse se concentre sur l’évaluation des solutions, et non seulement sur les difficultés rencontrées. C’est ce dernier élément qui distingue ceux qui se contentent de « lire la documentation » de ceux qui savent gérer cette stack sous pression.