forwardRef rajapinnan avulla komponentti voi tarjota DOM noodin pääkomponenetille ref:llä.

const SomeComponent = forwardRef(render)

Referenssi

forwardRef(render)

Kutsu forwardRef() -funktiota, jotta komponenttisi voi vastaanottaa ref:n ja välittää sen lapsikomponentille:

import { forwardRef } from 'react';

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

Katso lisää esimerkkejä alla.

Parametrit

  • render: Komponenttisi renderöintifunktio. React kutsuu tätä funktiota komponentin pääkomponentilta saamilla propseilla ja ref:lla. JSX, jonka palautat, on komponenttisi ulostulo.

Palautukset

forwardRef palauttaa React-komponentin, jonka voit renderöidä JSX:llä. Toisin kuin React-komponentit, jotka on määritelty tavallisina funktioina, forwardRef:n palauttama komponentti voi myös vastaanottaa ref propin.

Huomiot

  • Strict Modessa, React kutsuu renderöintifunktiotasi kahdesti auttaakseen sinua löytämään tahattomia epäpuhtauksia. Tämä on vain kehitystilassa tapahtuva käyttäytyminen, eikä vaikuta tuotantoon. Jos renderöintifunktiosi on puhdas (kuten sen pitäisi olla), tämä ei vaikuta komponenttisi logiikkaan. Toinen kutsuista jätetään huomiotta.

render funktio

forwardRef hyväksyy renderöintifunktion argumenttina. React kutsuu tätä funktiota props ja ref -argumenteilla:

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

Parametrit

  • props: Propsit, jotka pääkomponentti on välittänyt.

  • ref: ref attribuutti, jonka pääkomponentti on välittänyt. ref voi olla joko objekti tai funktio. Jos pääkomponentti ei ole välittänyt ref:iä, se on null. Sinun tulisi joko välittää saamasi ref toiselle komponentille tai välittää se useImperativeHandle:lle.

Palautukset

forwardRef palauttaa React komponentin, jonka voit renderöidä JSX:llä. Toisin kuin React komponentit, jotka on määritelty tavallisina funktioina, forwardRef:n palauttama komponentti voi myös vastaanottaa ref propin.


Käyttö

DOM noodin välittäminen pääkomponentille

Oletuksena jokaisen komponentin DOM noodit ovat yksityisiä. Joskus on kuitenkin hyödyllistä välittää DOM noodi pääkomponentille, esimerkiksi mahdollistaaksesi siihen kohdentamisen. Ottaaksesi tämän käyttöön, kääri komponenttisi forwardRef() -funktioon:

import { forwardRef } from 'react';

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

Saat ref -argumentin toisena argumenttina propsien jälkeen. Välitä se DOM noodiin, jonka haluat julkaista:

import { forwardRef } from 'react';

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

Tämän avulla pääkomponentti Form voi käyttää MyInput komponentin julkaisemaa <input> DOM noodia:

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

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

return (
<form>
<MyInput label="Enter your name:" ref={ref} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}

Form komponentti välittää ref:n MyInput:lle. MyInput komponentti välittää sen ref:n <input> selaimen tagille. Tämän seurauksena Form komponentti voi käyttää <input> DOM noodia ja kutsua focus() siihen.

Pidä mielessä, että ref:n julkaiseminen komponenttisi sisällä olevaan DOM noodin tekee sen vaikeammaksi muuttaa komponenttisi sisäistä rakennetta myöhemmin. Yleensä julkaiset DOM noodin ref:n uudelleen käytettävistä matalan tason komponenteista, kuten painikkeista tai tekstisyötteistä, mutta et tee sitä sovellustason komponenteille, kuten avatarille tai kommentille.

Kokeile esimerkkejä

Esimerkki 1 / 2:
Syöttökenttään kohdistaminen

Painiketta painaminen kohdistaa syöttökenttään. Form komponentti määrittelee ref:n ja välittää sen MyInput komponentille. MyInput komponentti välittää sen ref:n selaimen <input> tagille. Tämän avulla Form komponentti voi kohdistaa <input>:in.

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="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}


Refin välittäminen useiden komponenttien läpi

Instead of forwarding a ref to a DOM node, you can forward it to your own component like MyInput:

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

If that MyInput component forwards a ref to its <input>, a ref to FormField will give you that <input>:

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

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

return (
<form>
<FormField label="Enter your name:" ref={ref} isRequired={true} />
<button type="button" onClick={handleClick}>
Edit
</button>
</form>
);
}

The Form component defines a ref and passes it to FormField. The FormField component forwards that ref to MyInput, which forwards it to a browser <input> DOM node. This is how Form accesses that DOM node.

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="Enter your name:" ref={ref} isRequired={true} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}


Exposing an imperative handle instead of a DOM node

Instead of exposing an entire DOM node, you can expose a custom object, called an imperative handle, with a more constrained set of methods. To do this, you’d need to define a separate ref to hold the DOM node:

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

// ...

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

Pass the ref you received to useImperativeHandle and specify the value you want to expose to the 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} />;
});

If some component gets a ref to MyInput, it will only receive your { focus, scrollIntoView } object instead of the DOM node. This lets you limit the information you expose about your DOM node to the minimum.

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

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

  function handleClick() {
    ref.current.focus();
    // This won't work because the DOM node isn't exposed:
    // ref.current.style.opacity = 0.5;
  }

  return (
    <form>
      <MyInput label="Enter your name:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Edit
      </button>
    </form>
  );
}

Read more about using imperative handles.

Sudenkuoppa

Do not overuse refs. You should only use refs for imperative behaviors that you can’t express as props: for example, scrolling to a node, focusing a node, triggering an animation, selecting text, and so on.

If you can express something as a prop, you should not use a ref. For example, instead of exposing an imperative handle like { open, close } from a Modal component, it is better to take isOpen as a prop like <Modal isOpen={isOpen} />. Effects can help you expose imperative behaviors via props.


Vianmääritys

Komponenttini on kääritty forwardRef:iin, mutta ref siihen on aina null

This usually means that you forgot to actually use the ref that you received.

For example, this component doesn’t do anything with its ref:

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

To fix it, pass the ref down to a DOM node or another component that can accept a ref:

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

The ref to MyInput could also be null if some of the logic is conditional:

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

If showInput is false, then the ref won’t be forwarded to any node, and a ref to MyInput will remain empty. This is particularly easy to miss if the condition is hidden inside another component, like Panel in this example:

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