forwardRef permet à votre composant d’exposer un nœud DOM à son composant parent au travers d’une ref.

const SomeComponent = forwardRef(render)

Référence

forwardRef(render)

Appelez forwardRef() pour que votre composant reçoive une ref qu’il puisse transmette à un de ses composants enfants :

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
// ...
});

Voir dautres exemples ci-dessous.

Paramètres

  • render : la fonction de rendu de votre composant. React appellera cette fonction avec les props et la ref que votre composant aura reçu de son parent. Cette fonction renvoie, comme d’habitude, le JSX constitutif du composant.

Valeur renvoyée

forwardRef renvoie un composant React qui peut figurer dans un rendu JSX. Contrairement aux composants React définis par des fonctions classiques, un composant renvoyé par forwardRef pourra en prime accepter une prop ref.

Limitations

  • En mode strict, React appellera votre fonction composant deux fois afin de vous aider à repérer des impuretés accidentelles. Ce comportement est limité au développement et n’affecte pas la production. Une des valeurs renvoyées sera ignorée. Si votre fonction composant est pure (ce qui devrait être le cas), ça n’affectera en rien son comportement.

La fonction render

forwardRef accepte une fonction de rendu en argument. React appellera cette fonction avec props et ref :

const MyInput = forwardRef(function MyInput(props, ref) {
return (
<label>
{props.label}
<input ref={ref} />
</label>
);
});

Paramètres

  • props : les props passées par le composant parent.

  • ref : la prop ref passée par le composant parent. La ref peut être un objet ou une fonction. Si le composant parent n’a pas passé de ref, elle sera null. Vous pouvez soit passer la ref reçue à un autre composant soit la passer à useImperativeHandle.

Valeur renvoyée

forwardRef renvoie un composant React qui peut figurer dans un rendu JSX. Contrairement aux composants React définis par des fonctions classiques, un composant renvoyé par forwardRef pourra en prime accepter une prop ref.


Utilisation

Exposer un nœud DOM au composant parent

Par défaut, tous les nœuds DOM de votre composant sont privés. Ceci dit, il peut parfois être utile d’exposer un nœud DOM à votre parent — par exemple pour en permettre l’activation. Pour permettre ça, enrobez votre définition de composant avec forwardRef() :

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} />
</label>
);
});

Vous recevrez une ref comme second argument, juste après les props. Passez-la au nœud DOM que vous souhaitez exposer :

import { forwardRef } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
});

Ça permet au composant parent Form d’accéder au nœud DOM <input> exposé par MyInput :

function Form() {
const ref = useRef(null);

function handleClick() {
ref.current.focus();
}

return (
<form>
<MyInput label="Saisissez votre nom :" ref={ref} />
<button type="button" onClick={handleClick}>
Modifier
</button>
</form>
);
}

Le composant Form passe une ref à MyInput. Le composant MyInput transmet cette ref à la balise native <input>. Résultat, le composant Form peut accéder au nœud DOM <input> et appeler sa méthode focus().

Gardez à l’esprit qu’exposer une ref vers un nœud DOM au sein de votre composant peut vous embêter plus tard si vous souhaitez refondre la structure interne de celui-ci. Classiquement, vous exposerez des nœuds DOM depuis des composants réutilisables de bas niveau tels que des boutons ou des champs de saisie, mais vous éviterez de le faire pour des composants applicatifs comme un avatar ou un bloc de commentaire.

Exemples de transmission de ref

Exemple 1 sur 2 ·
Activer un champ de saisie

Un clic sur le bouton activera le champ de saisie. Le composant Form définit une ref qu’il passe au composant MyInput. Ce composant MyInput transmet la ref au <input> du navigateur. Ça permet au composant Form d’activer le <input>.

import { useRef } from 'react';
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <MyInput label="Saisissez votre nom :" ref={ref} />
      <button type="button" onClick={handleClick}>
        Modifier
      </button>
    </form>
  );
}


Transmettre une ref à travers plusieurs composants

Au lieu de transmettre la ref à un nœud DOM, vous avez parfois besoin de la transmettre à votre propre composant, comme MyInput :

const FormField = forwardRef(function FormField(props, ref) {
// ...
return (
<>
<MyInput ref={ref} />
...
</>
);
});

Si ce composant MyInput transmet une ref à son <input>, une ref à FormField vous donnera ce <input> :

function Form() {
const ref = useRef(null);

function handleClick() {
ref.current.focus();
}

return (
<form>
<FormField label="Saisissez votre nom :" ref={ref} isRequired={true} />
<button type="button" onClick={handleClick}>
Modifier
</button>
</form>
);
}

Le composant Form définit une ref et la passer au FormField . Le composant FormField transmet cette ref au MyInput, qui la transmet au nœud DOM <input>. C’est ainsi que Form accède à ce nœud DOM.

import { useRef } from 'react';
import FormField from './FormField.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
  }

  return (
    <form>
      <FormField label="Saisissez votre nom :" ref={ref} isRequired={true} />
      <button type="button" onClick={handleClick}>
        Modifier
      </button>
    </form>
  );
}


Exposer un point d’accès impératif plutôt qu’un nœud DOM

Au lieu d’exposer l’intégralité du nœud DOM, vous pouvez exposer un objet personnalisé qu’on appelle point d’accès impératif, doté d’un jeu plus restreint de méthodes. Pour cela, vous devez définir une ref séparée pour référencer le nœud DOM :

const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);

// ...

return <input {...props} ref={inputRef} />;
});

Passez la ref que vous avez reçue à useImperativeHandle et spécifiez la valeur que vous souhaitez exposer en tant que ref :

import { forwardRef, useRef, useImperativeHandle } from 'react';

const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);

useImperativeHandle(ref, () => {
return {
focus() {
inputRef.current.focus();
},
scrollIntoView() {
inputRef.current.scrollIntoView();
},
};
}, []);

return <input {...props} ref={inputRef} />;
});

Si un composant récupère la ref de MyInput, il ne recevra que votre objet { focus, scrollIntoView } au lieu du nœud DOM. Ça vous permet de limiter au minimum les parties du nœud DOM que vous exposez.

import { useRef } from 'react';
import MyInput from './MyInput.js';

export default function Form() {
  const ref = useRef(null);

  function handleClick() {
    ref.current.focus();
    // Ça ne marchera pas parce que le nœud DOM
    // n'est pas exposé :
    // ref.current.style.opacity = 0.5;
  }

  return (
    <form>
      <MyInput label="Saisissez votre nom :" ref={ref} />
      <button type="button" onClick={handleClick}>
        Modifier
      </button>
    </form>
  );
}

En savoir plus sur les points d’accès impératifs.

Piège

N’abusez pas des refs. Vous ne devriez utiliser des refs que pour des comportements impératifs qui ne peuvent pas être exprimés par des props : faire défiler jusqu’à un nœud, activer un nœud, déclencher une animation, sélectionner un texte, et ainsi de suite.

Si vous pouvez exprimer quelque chose sous forme de prop, n’utilisez pas une ref. Par exemple, plutôt que d’exposer un objet impératif du genre { open, close } depuis un composant Modal, préférez proposer une prop isOpen pour une utilisation du style <Modal isOpen={isOpen} />. Les Effets peuvent vous aider à exposer des comportements impératifs au travers de props.


Dépannage

Mon composant est enrobé par forwardRef, mais la ref que je reçois est toujours null

Ça signifie généralement que vous avez oublié d’utiliser effectivement la ref que vous avez reçue.

Par exemple, ce composant ne fait rien avec sa ref :

const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input />
</label>
);
});

Pour corriger ça, transmettez la ref au nœud DOM ou à un autre composant qui peut acccepter une ref :

const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input ref={ref} />
</label>
);
});

La ref à MyInput pourrait aussi être null si une partie de la logique était conditionnelle :

const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
{showInput && <input ref={ref} />}
</label>
);
});

Si showInput est false, alors la ref ne sera transmise à aucun nœud, et la ref à MyInput restera vide. C’est particulièrement difficile à repérer si la condition est enfouie dans un autre composant, comme Panel dans l’exemple ci-après :

const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
<Panel isExpanded={showInput}>
<input ref={ref} />
</Panel>
</label>
);
});