В чем разница между useRef и forwardRef

Ответ

useRef — это хук, создающий мутабельный объект с свойством current, которое можно изменять без перерисовки компонента. Используется для хранения DOM-элементов, таймеров и других значений.

forwardRef — это HOC, который позволяет передавать ref через компонент к дочернему элементу. Используется, когда нужно получить доступ к DOM-элементу внутри кастомного компонента.

Пример useRef:

const inputRef = useRef(null);
<input ref={inputRef} />

Пример forwardRef:

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

Ключевое отличие: useRef создает ref, а forwardRef передает его через промежуточные компоненты.

Ответ 18+ 🔞

О, смотри-ка, народ опять путает эти два хука, будто это одно и то же! Да нет же, ёпта, это как сравнивать молоток и гвоздь — оба в строительстве нужны, но задачи у них разные, блядь.

Вот useRef — это такой твой личный сейф в функциональном компоненте. Создаёшь ты его, и он тебе выдаёт объект с одним-единственным полем current. И главная фишка в чём? Что ты можешь туда что угодно пихать — DOM-элемент, таймер, какое-то промежуточное значение — и компонент от этого перерисовываться не будет, хоть ты тресни! Он мутабельный, как твоя старая футболка, которую жалко выбросить. Записал в current — и всё, сидит себе, пока сам не захочешь поменять.

const mySecretBox = useRef(null); // Вот тебе сейф, пустой пока
// ...
mySecretBox.current = document.getElementById('myInput'); // Положил в него DOM-ноду
// ...
console.log(mySecretBox.current.value); // Достал и прочитал, что там

А forwardRef — это вообще история другого полета. Это не хук для создания, а хук-обертка, ёбана! Представь, что ты делаешь свой красивый, стильный компонент FancyInput, но внутри него спрятан обычный <input />. И тебе, сука, надо дать родительскому компоненту прямой доступ к этому самому нативному инпуту — чтобы фокус поставить или значение прочитать. Вот тут-то forwardRef и приходит на помощь, как хитрая жопа. Он позволяет "прокинуть" этот ref сквозь твой компонент прямиком к тому элементу, который внутри.

// Без forwardRef родительский ref на этот компонент не сработает
const FancyInput = forwardRef((props, refFromParent) => {
  // А вот тут мы этот полученный ref прикрепляем куда надо
  return <input ref={refFromParent} className="fancy-as-hell" {...props} />;
});

// А в родителе используем как обычно
const Parent = () => {
  const inputRef = useRef(null); // Создали ref через useRef
  // И спокойно прокидываем в кастомный компонент
  return <FancyInput ref={inputRef} />;
};

Короче, резюмирую, чтобы в голове осело:

  • useRef — это создание мутабельного контейнера (ref-объекта). Инструмент для хранения штук, не влияющих на отрисовку.
  • forwardRef — это прокидывание уже существующего ref сквозь твой кастомный компонент к его внутреннему DOM-элементу или другому компоненту с ref.

Используются они часто вместе, как водка и закуска. Сначала useRef создал в родителе, потом forwardRef помог этот ref протащить через промежуточные слои к нужной цели. Вот и вся магия, никакого пиздеца.