forwardRef дазваляе кампаненту раскрыць вузел DOM для бацькоўскага кампанента праз ref.

const SomeComponent = forwardRef(render)

Даведка

forwardRef(render)

Выклічце forwardRef() каб ваш кампанент змог атрымаць рэф і перадаць яго даччынаму:

import { forwardRef } from 'react';

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

Болей прыкладаў глядзіце ніжэй.

Параметры

  • render: Функцыя рэндэрынгу вашага кампанента. React выклікае дадзеную функцыю, перадаючы пропсы і ref, перададзены ў ваш кампанент з бацькоўскага. JSX, які вяртае функцыя, будзе разметкай вашага кампанента.

Значэнні, якія вяртаюцца

forwardRef вяртае кампанент React, які можа быць адрэндэраны праз JSX. У адрозненні ад кампанентаў, вызначаных з дапамогай звычайных функцый, кампанент, вызначаны праз forwardRef, здольны таксама прымаць пропс ref.

Перасцярогі

  • У Строгім рэжыме, React будзе выклікаць функцыю рэндэрынгу двойчы каб дапамагчы выявіць магчымыя пабочныя эфекты. Такія паводзіны адбываюцца толькі ў асяроддзі для распрацоўкі і ніяк не ўплываюць на працоўнае. Калі функцыя вашага кампанента чыстая (якой яна мае быць), гэта не павінна ўплываць на логіку кампанента. Вынік аднаго з выклікаў будзе праігнараваны.

Функцыя render

forwardRef прымае функцыю рэндэрынгу як аргумент. React выклікае гэтую функцыю, перадаючы props і ref:

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

Параметры

  • props: Пропсы, перададзеныя з бацькоўскага кампанента.

  • ref: Атрыбут ref, перададзены з бацькоўскага кампанента. ref можа быць аб’ектам ці функцыяй. Калі бацькоўскі кампанент не перадаў рэф, значэннем будзе null. Вы маеце перадаць атрыманы ref у іншы кампанент ці ў useImperativeHandle.

Значэнні, якія вяртаюцца

forwardRef вяртае кампанент React, які можа быць адрэндэраны праз JSX. У адрозненні ад кампанентаў, вызначаных з дапамогай звычайных функцый, кампанент, вызначаны праз forwardRef здольны таксама прымаць пропс ref.


Выкарыстанне

Раскрыццё вузла DOM кампанента бацькоўскаму

Першапачаткова вузлы DOM кожнага з кампанентаў прыватныя. Але ж, часам бывае карысна адкрыць вузел для бацькоўскага кампанента. Напрыклад, каб дазволіць сфакусіравацца на ім. Каб гэта зрабіць, абгарніце вызначэнне вашага кампанента ў forwardRef():

import { forwardRef } from 'react';

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

Вы атрымаеце ref другім аргументам пасля пропсаў. Перадайце яго ў вузел DOM, які вы хочаце раскрыць:

import { forwardRef } from 'react';

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

Гэта дазволіць бацькоўскаму кампаненту Form атрымаць доступ да вузла DOM <input> раскрытага ў MyInput:

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

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

return (
<form>
<MyInput label="Увядзіце вашае імя:" ref={ref} />
<button type="button" onClick={handleClick}>
Рэдагаваць
</button>
</form>
);
}

Дадзены кампанент Form задае рэф для MyInput. Кампанент MyInput перанакіроўвае гэты рэф да тэга <input>. У выніку кампанент Form можа атрымаць доступ да вузла DOM <input> і выклікаць ягоны метад focus().

Памятайце, што раскрыццё рэфа вузла DOM унутры кампанента ўскладняе змену ўнутраных часцей кампанента пазней. Звычайна вам можа спатрэбіцца раскрыццё вузлоў DOM для нізкаўзроўневых кампанентаў, якія вы будзеце перавыкарыстоўваць, такіх як кнопкі ці палі ўводу тэксту, але не для болей высоўкаўзроўневых, такіх як аватары ці каментарыі.

Прыклады перанакіроўвання рэфа

Example 1 of 2:
Факусіраванне на полі ўводу тэксту

Націсканне кнопкі прывядзе да факусіравання на полі ўводу. Кампанент Form вызначае рэф і перадае яго ў кампанент MyInput. Кампанент MyInput перанакіроўвае гэты рэф да элемента <input>. Гэта дазваляе кампаненту Form зрабіць факусіраванне на <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="Увядзіце вашае імя:" ref={ref} />
      <button type="button" onClick={handleClick}>
        Рэдагаваць
      </button>
    </form>
  );
}


Перанакіроўванне рэфа цераз некалькі кампанентаў

Замест таго, каб перанакіроўваць ref у вузел DOM, вы можаце перанакіраваць яго ва ўласны кампанент, напрыклад MyInput:

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

Калі кампанент MyInput перанакіроўвае рэф у ягоны <input>, рэф ад FormField таксама будзе змяшчаць той <input>:

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

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

return (
<form>
<FormField label="Увядзіце вашае імя:" ref={ref} isRequired={true} />
<button type="button" onClick={handleClick}>
Рэдагаваць
</button>
</form>
);
}

Кампанент Form вызначае рэф і перадае яго ў FormField. Кампанент FormField перанакіроўвае рэф у кампанент MyInput, які ў сваю чаргу перанакіроўвае рэф у вузел DOM <input>. Такім чынам Form атрымлівае доступ да дадзенага вузла 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="Увядзіце вашае імя:" ref={ref} isRequired={true} />
      <button type="button" onClick={handleClick}>
        Рэдагаваць
      </button>
    </form>
  );
}


Раскрыццё загаднага кіравання замест вузла DOM

Замест таго, каб раскрываць вузел DOM цалкам, вы можаце раскрыць уласны аб’ект з абмежаваным наборам метадаў, які называюць загадным кіраваннем. Каб гэта зрабіць, спатрэбіцца вызначыць асобны рэф, у якім трымаць вузел DOM:

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

// ...

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

Перадайце атрыманы ref у useImperativeHandle і вызначце значэнні, якія вы хочаце раскрыць у 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} />;
});

Калі які-небудзь кампанент перадасць рэф у MyInput, ён атрымае толькі ваш аб’ект { focus, scrollIntoView } замест вузла DOM. Гэта дазваляе абмяжоўваць інфармацыю, якую вы хочаце раскрыць пра ваш вузел DOM, да мінімуму.

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

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

  function handleClick() {
    ref.current.focus();
    // Гэты код не спрацуе, бо вузел DOM не раскрыты:
    // ref.current.style.opacity = 0.5;
  }

  return (
    <form>
      <MyInput placeholder="Увядзіце вашае імя" ref={ref} />
      <button type="button" onClick={handleClick}>
        Рэдагаваць
      </button>
    </form>
  );
}

Даведайцеся больш пра выкарыстанне загаднага кіравання.

Pitfall

Не выкарыстоўвайце рэфы болей, чым трэба. Варта выкарыстоўваць рэфы толькі для загаднага кіравання, якое нельга зрабіць праз пропсы: напрыклад, каб прагартаць да вузла, сфакусіравацца на ім, пачаць анімацыю, вылучыць тэкст і г.д.

Калі штосьці можна выразіць пропсамі, не варта выкарыстоўваць для гэтага рэфы. Напрыклад, замест раскрыцця падобнага загаднага кіравання: { open, close } з кампанента Modal, лепей прымаць isOpen як пропс, напрыклад: <Modal isOpen={isOpen} />. Эфекты могуць дапамагчы раскрыць загаднае кіраванне ў выглядзе пропсаў.


Дыягностыка непаладак

Мой кампанент абгорнуты ў forwardRef, але ref заўсёды null

Звычайна гэта азначае, што вы забылі скарыстаць ref, які вы атрымалі.

Напрыклад, вось гэты кампанент нічога не робіць з атрыманым ref:

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

Каб гэта вырашыць, перадайце ref далей вузлу DOM ці іншаму кампаненту, што прымае рэф:

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

ref у MyInput таксама можа быць null калі сустракаецца ўмоўная логіка:

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

Калі значэнне showInput роўнае false, рэф не будзе перададзены ніводнаму вузлу, і рэф MyInput застанецца пустым. Падобнае лёгка прапусціць, калі ўмова схаваная ўнутры іншага кампанента, як у гэтым кампаненце Panel у прыкладзе:

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