Menu

forwardRef en React : transmettre des refs, ref en prop

forwardRef permet à un composant de recevoir une ref de son parent et de l'attacher à un nœud DOM interne. Dans React 19, les composants fonctions reçoivent ref comme une prop normale, le nouveau code n'a donc plus besoin de forwardRef. Voyez les deux versions en action, plus useImperativeHandle et les types TypeScript.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

forwardRef permet à un composant parent de faire passer une ref à travers votre composant jusqu'à un élément DOM interne, pour que le parent puisse appeler focus(), scrollIntoView() et des méthodes similaires sur cet élément. Dans React 19, vous n'en avez plus besoin : les composants fonctions reçoivent ref comme une prop ordinaire. forwardRef fonctionne toujours, et vous le verrez dans la plupart du code existant, cette page montre donc les deux.

forwardRef prend une fonction de rendu avec deux arguments : props, et la ref passée par le parent. ref n'est pas dans props ; elle arrive séparément. Cliquez sur Focus et la console confirme que inputRef.current est le vrai nœud <input>.

La façon de React 19 : ref est une prop

Depuis React 19, un composant fonction reçoit ref dans ses props comme n'importe quelle autre prop. Pas d'enveloppe, pas de second argument :

Tapez quelque chose et cliquez sur Log value : le parent lit le texte actuel du champ directement depuis le nœud DOM. Supprimez ref={ref} de l'<input> et cliquez sur Focus : la console affiche une erreur, car inputRef.current vaut maintenant null.

forwardRef (toutes versions)Prop ref (React 19+)
Comment le composant reçoit la refSecond argument, (props, ref)props.ref
Enveloppe nécessaireOuiNon
Fonctionne avec useImperativeHandleOuiOui
StatutFonctionne, obsolescence prévueLa façon d'écrire le nouveau code

Une ref ignorée par l'enfant reste null

Passer ref à un composant ne fait rien en soi. Le composant doit la placer sur un élément. S'il ne le fait pas, ref.current reste null :

La console affiche Forgetful ref: null et Careful ref: INPUT. C'est la raison la plus fréquente d'un « ref.current is null » sur un composant personnalisé. Avant React 19, Careful aurait aussi valu null sans enveloppe forwardRef, puisque la ref n'était jamais transmise à la fonction.

useImperativeHandle : exposer une API réduite

Donner au parent le nœud DOM entier lui permet de tout faire : changer les styles, retirer des enfants, lire des valeurs que vous vouliez garder privées. useImperativeHandle vous permet de décider à la place de ce que contient ref.current :

Le dernier bouton affiche ["focus", "clear"] : le parent reçoit ces deux méthodes et rien d'autre, pas le nœud du champ. Ajoutez une méthode select() à l'objet et un bouton qui l'appelle, et le parent gagne exactement une capacité de plus.

L'enfant garde sa propre ref, inputRef, pour le vrai champ, et le handle l'enveloppe. Le troisième argument est un tableau de dépendances, comme celui de useEffect : le handle est reconstruit quand ces valeurs changent.

Utilisez-le avec parcimonie. La plupart des choses qu'un parent veut faire (ouvrir, fermer, afficher une erreur) se passent mieux en props, comme isOpen ou error. Les méthodes impératives servent aux actions qui n'ont pas de prop naturelle : donner le focus, faire défiler, lancer une vidéo, démarrer une animation.

Utiliser aussi la ref dans l'enfant

Parfois, l'enfant a besoin du même nœud DOM pour son propre travail, par exemple pour le mesurer ou lui donner le focus après une erreur, tandis que le parent détient aussi une ref vers lui. Un attribut ref ne peut prendre qu'une seule valeur, fusionnez donc les deux avec une ref callback :

function AutoGrowTextarea({ ref, ...props }) {
    const localRef = useRef(null);

    function setRefs(node) {
        localRef.current = node;
        if (typeof ref === 'function') ref(node);
        else if (ref) ref.current = node;
    }

    return <textarea ref={setRefs} {...props} />;
}

La ref du parent peut être un objet issu de useRef ou une fonction, gérez donc les deux cas. Quand le parent n'a besoin que de quelques actions, useImperativeHandle ci-dessus est le choix le plus propre, car l'enfant garde le nœud pour lui.

Transmettre une ref à travers plusieurs couches

Une ref voyage un composant à la fois. Si Form affiche Field, qui affiche TextInput, qui affiche l'<input>, chacun de ces composants doit transmettre ref au suivant. Dans React 19, c'est une prop de plus à transmettre (<TextInput ref={ref} />) ; avec forwardRef, chaque couche devait être enveloppée. Décomposer les props ({...props}) ne la transporte pas dans les anciennes versions, car avant React 19 ref ne faisait jamais partie de props.

Les composants de design system sont l'endroit habituel où cela compte : un Button, un Input ou un Select qui enveloppe un élément natif doit transmettre sa ref, pour que l'application qui l'utilise puisse lui donner le focus, le mesurer ou le confier à une bibliothèque qui positionne des popovers.

Migrer depuis forwardRef

Le changement est mécanique : retirez l'enveloppe et lisez ref depuis les props.

// Before
const Button = forwardRef(function Button({ variant, ...props }, ref) {
    return <button ref={ref} className={variant} {...props} />;
});

// After (React 19)
function Button({ variant, ref, ...props }) {
    return <button ref={ref} className={variant} {...props} />;
}

Rien ne presse. forwardRef continue de fonctionner dans React 19, et une bibliothèque qui doit prendre en charge React 18 doit le garder, car React 18 ne passe pas ref comme prop. Les composants classes ne sont pas concernés : une ref sur un composant classe pointe toujours vers l'instance du composant.

TypeScript

Avec forwardRef, les arguments de type viennent dans l'ordre : type de la ref, puis props :

import { forwardRef } from 'react';

type FancyInputProps = { label: string };

const FancyInput = forwardRef<HTMLInputElement, FancyInputProps>(
    function FancyInput({ label }, ref) {
        return <input ref={ref} aria-label={label} />;
    }
);

Dans React 19, typez ref comme n'importe quelle autre prop. ComponentProps<'input'> l'inclut déjà :

import { useImperativeHandle, useRef, type ComponentProps, type Ref } from 'react';

function FancyInput(props: ComponentProps<'input'>) {
    return <input {...props} />;
}

type SearchHandle = { focus: () => void; clear: () => void };

function SearchBox({ ref }: { ref?: Ref<SearchHandle> }) {
    const inputRef = useRef<HTMLInputElement>(null);
    useImperativeHandle(ref, () => ({
        focus: () => inputRef.current?.focus(),
        clear: () => {
            if (inputRef.current) inputRef.current.value = '';
        },
    }));
    return <input ref={inputRef} />;
}

// In the parent
const searchRef = useRef<SearchHandle>(null);

La page sur useRef traite des refs elles-mêmes : accès au DOM, valeurs qui persistent sans rendu et ref callbacks. Pour typer les composants en général, voir React avec TypeScript.

Questions fréquentes

À quoi sert forwardRef dans React ?

Il enveloppe un composant fonction pour qu'une ref donnée par le parent arrive au composant en second argument, (props, ref). Le composant place ensuite cette ref sur un nœud DOM, pour que le parent puisse appeler dessus des méthodes comme focus().

forwardRef est-il obsolète dans React 19 ?

Pas encore, et il fonctionne toujours. React 19 passe ref aux composants fonctions comme une prop ordinaire, le nouveau code n'en a donc pas besoin, et l'équipe React a annoncé son intention de déclarer forwardRef obsolète dans une future version.

Pourquoi ma ref vaut-elle null sur un composant personnalisé ?

Le composant a reçu la ref mais ne l'a placée sur aucun élément. Attachez-la à un nœud DOM interne : <input ref={ref} />. Avant React 19, la ref n'était pas transmise du tout si le composant n'était pas enveloppé dans forwardRef.

À quoi sert useImperativeHandle ?

Il remplace ce que le parent voit dans ref.current. Au lieu du nœud DOM entier, vous renvoyez un objet avec seulement les méthodes que vous choisissez, comme focus et clear.

Les composants classes reçoivent-ils ref en prop dans React 19 ?

Non. Une ref sur un composant classe pointe toujours vers l'instance du composant. Le changement ne concerne que les composants fonctions.

Illustration des langages de programmation de Coddy

Apprendre à coder avec Coddy

COMMENCER