Что такое React.forwardRef в React?

MiddleReact · Frontend·Обновлено 15 июля 2026
Коротко
React.forwardRef — это API, которое позволяет функциональному компоненту принять ref от родителя и перенаправить его на DOM-элемент или дочерний компонент внутри себя. Без forwardRef ref, переданный на компонент, не достигает его внутренних элементов.

React.forwardRef

По умолчанию атрибут ref не является обычным пропом: React обрабатывает его особым образом и не передаёт внутрь компонента. Если родитель хочет получить ссылку на DOM-узел внутри дочернего компонента, необходимо явно «пробросить» ref с помощью React.forwardRef.

Синтаксис

React.forwardRef принимает функцию рендера с двумя аргументами — props и ref — и возвращает React-компонент:

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

Теперь родитель может создать ref и получить прямой доступ к DOM-элементу <input>:

const ref = useRef<HTMLInputElement>(null);
<MyInput ref={ref} placeholder="Введите текст" />;
// ref.current — это сам <input>

Зачем это нужно

ForwardRef решает задачи, где родителю нужен императивный доступ к внутреннему узлу:

  • программный фокус (ref.current.focus())
  • измерение размеров и позиции элемента
  • интеграция со сторонними библиотеками, работающими с DOM напрямую
  • анимации через GSAP, Framer Motion и т.п.

Комбинация с useImperativeHandle

Часто forwardRef используют вместе с useImperativeHandle, чтобы не отдавать родителю весь DOM-узел, а экспортировать только нужный API:

const FancyInput = React.forwardRef((props, ref) => {
  const inputRef = useRef<HTMLInputElement>(null);

  // Родитель получит только метод focus, а не весь DOM-элемент
  useImperativeHandle(ref, () => ({
    focus: () => inputRef.current?.focus(),
    clear: () => {
      if (inputRef.current) inputRef.current.value = '';
    },
  }));

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

Родитель работает с безопасным интерфейсом:

type FancyInputHandle = { focus: () => void; clear: () => void };

const ref = useRef<FancyInputHandle>(null);
<FancyInput ref={ref} />;
ref.current?.focus();
ref.current?.clear();

displayName для отладки

При использовании forwardRef компонент в DevTools отображается как ForwardRef. Чтобы дать ему читаемое имя, установите displayName:

const MyInput = React.forwardRef((props, ref) => <input ref={ref} {...props} />);
MyInput.displayName = 'MyInput';

React 19 и новый подход

Начиная с React 19, ref передаётся как обычный проп и forwardRef больше не нужен для новых компонентов. Однако в проектах на React 16–18 forwardRef остаётся стандартным решением и встречается повсеместно.

Когда НЕ нужен forwardRef

  • Если родитель не нуждается в доступе к DOM — не добавляйте forwardRef без причины.
  • Для передачи данных между компонентами используйте пропы, контекст или стейт-менеджер, а не ref.

Что хочет услышать интервьюер

Понимание того, почему ref не является обычным пропом и не передаётся автоматически

Знание синтаксиса React.forwardRef и аргументов функции рендера (props, ref)

Умение объяснить практические сценарии применения: фокус, анимации, интеграция с DOM-библиотеками

Знание useImperativeHandle как способа ограничить экспортируемый API компонента

Осведомлённость об изменениях в React 19, где ref стал обычным пропом

Пример: Базовый проброс ref в DOM-элемент

import React, { useRef } from 'react';

// Компонент принимает ref и кидает его прямо на <input>
const TextInput = React.forwardRef<HTMLInputElement, React.InputHTMLAttributes<HTMLInputElement>>(
  (props, ref) => {
    return <input ref={ref} className="text-input" {...props} />;
  }
);
TextInput.displayName = 'TextInput';

// Использование в родителе
function Form() {
  const inputRef = useRef<HTMLInputElement>(null);

  const handleFocus = () => {
    // Прямой доступ к DOM-узлу
    inputRef.current?.focus();
  };

  return (
    <div>
      <TextInput ref={inputRef} placeholder="Имя" />
      <button onClick={handleFocus}>Сфокусировать</button>
    </div>
  );
}

Пример: forwardRef + useImperativeHandle — безопасный API

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

// Описываем только те методы, которые хотим отдать родителю
type VideoPlayerHandle = {
  play: () => void;
  pause: () => void;
  seek: (seconds: number) => void;
};

const VideoPlayer = forwardRef<VideoPlayerHandle, { src: string }>(
  ({ src }, ref) => {
    const videoRef = useRef<HTMLVideoElement>(null);

    useImperativeHandle(ref, () => ({
      play: () => videoRef.current?.play(),
      pause: () => videoRef.current?.pause(),
      // Родитель не может напрямую менять volume, currentTime и т.д.
      seek: (seconds) => {
        if (videoRef.current) videoRef.current.currentTime = seconds;
      },
    }));

    return <video ref={videoRef} src={src} />;
  }
);
VideoPlayer.displayName = 'VideoPlayer';

// Родитель управляет плеером через ограниченный интерфейс
function App() {
  const playerRef = useRef<VideoPlayerHandle>(null);

  return (
    <>
      <VideoPlayer ref={playerRef} src="/video.mp4" />
      <button onClick={() => playerRef.current?.play()}>Play</button>
      <button onClick={() => playerRef.current?.seek(30)}>+30 сек</button>
    </>
  );
}

Типичные ошибки

Попытка использовать ref-атрибут на функциональном компоненте без forwardRef и непонимание, почему ref равен null

Забывают установить displayName, что затрудняет отладку в React DevTools

Путают forwardRef с useImperativeHandle — объясняют их как взаимозаменяемые, хотя они решают разные задачи

Пробрасывают ref на корневой DOM-узел, когда нужно было скрыть детали реализации через useImperativeHandle

Не знают, что в React 19 forwardRef устарел и ref теперь передаётся как обычный проп

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.5
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее