forwardRef
forwardRef
permite que seu componente exponha um nó do DOM para o componente pai com um ref.
const SomeComponent = forwardRef(render)
Referência
forwardRef(render)
Chame forwardRef()
para permitir que seu componente receba um ref e o encaminhe para um componente filho:
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
// ...
});
Parâmetros
render
: A função de renderização para seu componente. O React chama essa função com os props e oref
que seu componente recebeu do seu pai. O JSX que você retornar será a saída do seu componente.
Retornos
forwardRef
retorna um componente React que você pode renderizar em JSX. Diferentemente dos componentes React definidos como funções simples, um componente retornado por forwardRef
também é capaz de receber um prop ref
.
Ressalvas
- No Modo Estrito, o React chamará sua função de renderização duas vezes para ajudar você a encontrar impurezas acidentais. Esse é um comportamento exclusivo para desenvolvimento e não afeta a produção. Se sua função de renderização for pura (como deve ser), isso não deve afetar a lógica do seu componente. O resultado de uma das chamadas será ignorado.
Função render
forwardRef
aceita uma função de renderização como argumento. O React chama essa função com props
e ref
:
const MyInput = forwardRef(function MyInput(props, ref) {
return (
<label>
{props.label}
<input ref={ref} />
</label>
);
});
Parâmetros
-
props
: Os props passados pelo componente pai. -
ref
: O atributoref
passado pelo componente pai. Oref
pode ser um objeto ou uma função. Se o componente pai não passou um ref, seránull
. Você deve passar oref
que receber a outro componente, ou passá-lo parauseImperativeHandle
.
Retornos
forwardRef
retorna um componente React que você pode renderizar em JSX. Diferentemente dos componentes React definidos como funções simples, o componente retornado por forwardRef
é capaz de aceitar um prop ref
.
Uso
Expondo um nó do DOM para o componente pai
Por padrão, os nós do DOM de cada componente são privados. No entanto, às vezes é útil expor um nó do DOM para o pai - por exemplo, para permitir que ele seja focado. Para optar por isso, envolva a definição do seu componente em forwardRef()
:
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} />
</label>
);
});
Você receberá um ref como o segundo argumento após os props. Passe-o para o nó do DOM que você deseja expor:
import { forwardRef } from 'react';
const MyInput = forwardRef(function MyInput(props, ref) {
const { label, ...otherProps } = props;
return (
<label>
{label}
<input {...otherProps} ref={ref} />
</label>
);
});
Isso permite que o componente pai Form
acesse o nó DOM <input>
exposto por MyInput
:
function Form() {
const ref = useRef(null);
function handleClick() {
ref.current.focus();
}
return (
<form>
<MyInput label="Digite seu nome:" ref={ref} />
<button type="button" onClick={handleClick}>
Editar
</button>
</form>
);
}
Esse componente Form
passa um ref para MyInput
. O componente MyInput
encaminha esse ref para a tag do navegador <input>
. Como resultado, o componente Form
pode acessar esse nó DOM <input>
e chamar focus()
nele.
Lembre-se de que expor um ref para o nó do DOM dentro do seu componente torna mais difícil alterar os internals do seu componente posteriormente. Você normalmente exporá nós do DOM de componentes reutilizáveis de baixo nível, como botões ou campos de texto, mas não fará isso para componentes de nível de aplicativo, como um avatar ou um comentário.
Example 1 of 2: Focando um campo de texto
Clicar no botão focará o campo de entrada. O componente Form
define um ref e o passa para o componente MyInput
. O componente MyInput
encaminha esse ref para o navegador <input>
. Isso permite que o componente Form
foque o <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="Digite seu nome:" ref={ref} /> <button type="button" onClick={handleClick}> Editar </button> </form> ); }
Encaminhando um ref através de múltiplos componentes
Em vez de encaminhar um ref
para um nó do DOM, você pode encaminhá-lo para seu próprio componente, como MyInput
:
const FormField = forwardRef(function FormField(props, ref) {
// ...
return (
<>
<MyInput ref={ref} />
...
</>
);
});
Se esse componente MyInput
encaminhar um ref para seu <input>
, um ref para FormField
lhe dará esse <input>
:
function Form() {
const ref = useRef(null);
function handleClick() {
ref.current.focus();
}
return (
<form>
<FormField label="Digite seu nome:" ref={ref} isRequired={true} />
<button type="button" onClick={handleClick}>
Editar
</button>
</form>
);
}
O componente Form
define um ref e o passa para FormField
. O componente FormField
encaminha esse ref para MyInput
, que o encaminha para um nó DOM <input>
do navegador. Assim é como Form
acessa esse nó 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="Digite seu nome:" ref={ref} isRequired={true} /> <button type="button" onClick={handleClick}> Editar </button> </form> ); }
Expondo um manipulador imperativo em vez de um nó do DOM
Em vez de expor um nó do DOM inteiro, você pode expor um objeto personalizado, chamado de manipulador imperativo, com um conjunto de métodos mais restrito. Para fazer isso, você precisaria definir um ref separado para armazenar o nó do DOM:
const MyInput = forwardRef(function MyInput(props, ref) {
const inputRef = useRef(null);
// ...
return <input {...props} ref={inputRef} />;
});
Passe o ref
que recebeu para useImperativeHandle
e especifique o valor que você deseja expor ao 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} />;
});
Se algum componente obter um ref para MyInput
, ele receberá apenas seu objeto { focus, scrollIntoView }
em vez do nó do DOM. Isso permite que você limite as informações que expõe sobre seu nó do DOM ao mínimo.
import { useRef } from 'react'; import MyInput from './MyInput.js'; export default function Form() { const ref = useRef(null); function handleClick() { ref.current.focus(); // Isso não funcionará porque o nó do DOM não está exposto: // ref.current.style.opacity = 0.5; } return ( <form> <MyInput placeholder="Digite seu nome" ref={ref} /> <button type="button" onClick={handleClick}> Editar </button> </form> ); }
Leia mais sobre o uso de manipuladores imperativos.
Solução de Problemas
Meu componente está envolto em forwardRef
, mas o ref
para ele está sempre null
Isso geralmente significa que você esqueceu de realmente usar o ref
que recebeu.
Por exemplo, este componente não faz nada com seu ref
:
const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input />
</label>
);
});
Para corrigir, passe o ref
para um nó do DOM ou outro componente que possa aceitar um ref:
const MyInput = forwardRef(function MyInput({ label }, ref) {
return (
<label>
{label}
<input ref={ref} />
</label>
);
});
O ref
para MyInput
também poderia ser null
se alguma lógica for condicional:
const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
{showInput && <input ref={ref} />}
</label>
);
});
Se showInput
for false
, então o ref não será encaminhado para nenhum nó, e um ref para MyInput
permanecerá vazio. Isso é particularmente fácil de perder se a condição estiver oculta dentro de outro componente, como Panel
neste exemplo:
const MyInput = forwardRef(function MyInput({ label, showInput }, ref) {
return (
<label>
{label}
<Panel isExpanded={showInput}>
<input ref={ref} />
</Panel>
</label>
);
});