Путешествие в мир рефлексов и переадресации в React

Во многих проектах React нам необходимо напрямую взаимодействовать с DOM.
Например, когда мы хотим установить фокус на элемент. Как это сделать в React?

Давайте начнем с создания пользовательского компонента ввода.

function MyInput({ ...props }: React.InputHTMLAttributes<HTMLInputElement>) {
  return <input {...props} />;
}
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь мы хотим получить фокус после установки компонента. Для этого нам нужно получить доступ к элементу DOM.
React refs — это способ получить ссылку на узел DOM. Мы используем хук useRef для создания объекта ref под названием myInputRef. Затем мы добавляем ссылку к элементу ввода.
myInputRef позволяет нам вызвать метод focus на элементе DOM. Поскольку мы хотим получить фокус после его установки, нам нужно использовать хук useEffect. В итоге мы получаем следующий код:

function MyInput({ ...props }: React.InputHTMLAttributes<HTMLInputElement>) {
  const myInputRef = React.useRef<HTMLInputElement>(null);
  React.useEffect(() => {
    if (myInputRef.current) {
      myInputRef.current.focus();
    }
  }, []);
  return <input ref={myInputRef} {...props} />;
}
Вход в полноэкранный режим Выход из полноэкранного режима

Представьте, что наша цель — создать универсальный пользовательский компонент. Мы не хотим получать фокус каждый раз, когда используем наш React-компонент. Идея заключается в том, чтобы быть более гибкими. Мы хотели бы передать ссылку и позволить родительскому компоненту решать, что делать (устанавливать фокус или нет).
Наивный подход заключается в том, чтобы добавить свойство ref к MyInput.

function MyInput({
  ref,
  ...props
}: React.DetailedHTMLProps<React.InputHTMLAttributes<HTMLInputElement>, HTMLInputElement> ) {
  return <input ref={ref} {...props} />;
}
Вход в полноэкранный режим Выход из полноэкранного режима

Вот компонент App, который использует MyInput:

function App() {
  const inputRef = useRef<HTMLInputElement>(null);

  React.useEffect(() => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);

  return (
    <div>
      <MyInput ref={inputRef} />
    </div>
  );
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Это не работает, и мы получаем следующее предупреждение:

Предупреждение: MyInput: ref не является реквизитом. Попытка получить к нему доступ приведет к возврату undefined. Если вам нужно получить доступ к тому же значению в дочернем компоненте, вы должны передать его как другой реквизит.

Мы не можем использовать реквизит с названием ref, поэтому давайте переименуем его. Вот как будет выглядеть код:

function MyInput({
  myRef,
  ...props
}: React.InputHTMLAttributes<HTMLInputElement> & {
  myRef: React.Ref<HTMLInputElement>;
}) {
  return <input ref={myRef} {...props} />;
}

function App() {
  const inputRef = useRef<HTMLInputElement>(null);

  React.useEffect(() => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);

  return (
    <div>
      <MyInput myRef={inputRef} />
    </div>
  );
}
Вход в полноэкранный режим Выйти из полноэкранного режима

Это работает. Но, согласно документации React, это не рекомендуемое решение. Компонент должен использовать свойство ref, чтобы быть совместимым с другими компонентами и библиотеками.
Ref недоступно в props, поэтому мы должны обернуть компонент в forwardRef. Теперь наша функция-компонент принимает ref в качестве второго аргумента.

Давайте рефакторим наш код, чтобы вместо этого использовать forwardRef. Вот окончательная версия:

type MyInputProps = React.DetailedHTMLProps<
  React.InputHTMLAttributes<HTMLInputElement>,
  HTMLInputElement
>;
const MyInput = React.forwardRef<HTMLInputElement, MyInputProps>(function (
  { ...props },
  ref
) {
  return <input ref={ref} {...props} />;
});

function App() {
  const inputRef = useRef<HTMLInputElement>(null);

  React.useEffect(() => {
    if (inputRef.current) {
      inputRef.current.focus();
    }
  }, []);

  return (
    <div>
      <MyInput ref={inputRef} />
    </div>
  );
}
Вход в полноэкранный режим Выход из полноэкранного режима

Эта техника автоматической передачи ссылки через компонент к одному из его дочерних элементов называется переадресацией.

Надеюсь, это дало вам представление о хорошем способе использования React forwardRef в Typescript.

Оцените статью
Procodings.ru
Добавить комментарий