Все, что вам нужно знать о React Ref: от основ до продвинутых техник.

Ссылки — это мощная функция в React, которая позволяет вам получать доступ к элементам DOM и управлять ими или сохранять изменяемые значения. В этой статье мы рассмотрим концепцию ссылок в React и дадим вам подробное представление о том, как эффективно использовать их в ваших приложениях.

Понимание React Refs

Ссылки в React — это ссылки либо на элементы DOM, либо на изменяемые значения. В прошлом рефы в основном использовались для взаимодействия с DOM, но с появлением React Hooks их использование расширилось и стало охватывать и другие цели. Реф, по своей сути, это просто ссылка на что-то.

Давайте начнем с примера использования ссылки для отслеживания состояния нажатия кнопки:

import React, { useRef } from 'react';

const ButtonComponent = () => {
  const buttonRef = useRef(false);
  const handleClick = () => {
    buttonRef.current = true;
  };
  return (
    <div>
      <button onClick={handleClick}>Click Me</button>
      {buttonRef.current && <p>Button clicked!</p>}
    </div>
  );
};

В этом примере мы используем хук useRef для создания объекта ссылки buttonRef и инициализируем его значением false. При нажатии кнопки функция handleClick обновляет свойство current ссылки на true. Затем мы можем условно отобразить сообщение на основе значения ref.

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

Использование ссылок в качестве переменных экземпляра

Одним из распространенных вариантов использования ссылок является использование переменных экземпляра в функциональных компонентах. Они позволяют вам отслеживать некоторую форму состояния, которая не должна вызывать повторный рендеринг. Давайте рассмотрим пример, в котором мы хотим определить, был ли компонент визуализирован в первый раз или он был повторно визуализирован:

import React, { useEffect, useRef } from 'react';

const Component = () => {
  const isFirstRender = useRef(true);
  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false;
    } else {
      console.log('Component re-rendered');
    }
  });
  return <p>Component content</p>;
};

В этом примере мы инициализируем свойство current ссылки значением true, потому что мы предполагаем, что компонент начинается с его первого рендеринга. Затем в хуке useEffect мы обновляем значение ссылки после первоначального рендеринга. Это позволяет нам различать первый рендеринг и последующие повторные рендеры.

Используя эту технику, мы можем создать хук useEffect, который запускает свою логику только для каждого обновления компонента, исключая первоначальный рендеринг. Это может быть ценной функцией в определенных сценариях.

Помните, что использование ссылок в качестве переменных экземпляра не является широко используемым шаблоном и, как правило, в этом нет необходимости. Однако это может быть полезным инструментом в определенных случаях, когда вам нужно отслеживать состояние, не запуская повторный рендеринг.

Refs и DOM

Теперь давайте углубимся в особенность ссылок в React: DOM. Чаще всего вы будете использовать функцию ref React, когда вам нужно взаимодействовать с элементами HTML. Хотя React продвигает декларативный подход, бывают случаи, когда требуется императивный доступ к DOM. Ссылки позволяют императивно взаимодействовать с DOM.

Давайте рассмотрим пример использования ссылки для взаимодействия с элементом DOM:

import React, { useRef } from 'react';

const TextInput = () => {
  const inputRef = useRef(null);
  const handleFocus = () => {
    inputRef.current.focus();
  };
  return (
    <div>
      <input type="text" ref={inputRef} />
      <button onClick={handleFocus}>Focus Input</button>
    </div>
  );
};

В этом примере мы создаем ссылку с помощью хука useRef и инициализируем ее с помощью null. Затем мы присваиваем эту ссылку атрибуту ref элемента <input>. Теперь свойство inputRef.current содержит ссылку на фактический DOM-узел входного элемента.

При нажатии кнопки вызывается функция handleFocus, которая использует метод focus() для узла DOM, на который ссылается inputRef.current. Это устанавливает фокус на элементе ввода, позволяя пользователю немедленно начать печатать.

Это демонстрирует, как мы можем использовать refs для взаимодействия с API HTML-элементов в императивной манере, что иногда необходимо для конкретных случаев использования.

Другим распространенным вариантом использования ссылок является чтение значений из узлов DOM. Допустим, мы хотим отобразить ширину элемента в качестве заголовка страницы:

import React, { useEffect, useRef } from 'react';
const ElementSize = () => {
  const elementRef = useRef(null);
  useEffect(() => {
    const element = elementRef.current;
    const width = element.offsetWidth;
    document.title = `Element Width: ${width}px`;
  }, []);
  return <div ref={elementRef}>Resizable Element</div>;
};

В этом примере мы создаем ссылку с помощью хука useRef и назначаем ее атрибуту ref элемента <div>. Внутри хука useEffect мы получаем доступ к узлу DOM, используя elementRef.current, и получаем его ширину, используя offsetWidth. Затем мы устанавливаем заголовок страницы, чтобы включить значение ширины.

Используя пустой массив зависимостей [], мы гарантируем, что этот эффект запустится только один раз, после первоначального рендеринга. Таким образом, мы получаем ширину элемента и отображаем ее как заголовок страницы.

Если мы хотим обновлять размер элемента всякий раз, когда изменяется определенная переменная состояния, мы можем включить эту переменную в массив зависимостей:

import React, { useEffect, useRef, useState } from 'react';

const ElementSize = () => {
  const elementRef = useRef(null);
  const [text, setText] = useState('');
  useEffect(() => {
    const element = elementRef.current;
    const width = element.offsetWidth;
    document.title = `Element Width: ${width}px`;
  }, [text]);
  return (
    <div ref={elementRef}>
      <input type="text" value={text} onChange={e => setText(e.target.value)} />
    </div>
  );
};

В этом обновленном примере мы вводим переменную состояния text и элемент ввода, который изменяет свое значение. Включив text в массив зависимостей хука useEffect, мы гарантируем, что эффект будет работать не только после первоначального рендеринга, но и при каждом изменении переменной состояния text. Это позволяет нам обновлять размер элемента и отражать его в заголовке страницы по мере того, как пользователь вводит текст.

До сих пор мы использовали хук useEffect для работы с объектом ref. Однако мы можем добиться того же результата, используя callback ref.

Обратный звонок

Обратные ссылки предоставляют альтернативный подход к работе с ссылками, устраняя необходимость в хуках useEffect и useRef. С помощью ссылки обратного вызова вы можете получить доступ к узлу DOM при каждом рендеринге без использования дополнительных хуков:

import React, { useRef } from 'react';

const TextInput = () => {
  const inputRef = useRef(null);
  const handleFocus = () => {
    inputRef.current.focus();
  };
  const setRef = node => {
    inputRef.current = node;
  };
  return (
    <div>
      <input type="text" ref={setRef} />
      <button onClick={handleFocus}>Focus Input</button>
    </div>
  );
};

В этом примере мы определяем функцию обратного вызова setRef, которая принимает узел в качестве аргумента. Внутри функции мы назначаем узел свойству current объекта inputRef. Передавая setRef в качестве атрибута ref элементу <input>, React будет вызывать эту функцию с фактическим узлом DOM всякий раз, когда он будет отображаться.

С рефами обратного вызова у вас есть доступ к узлу DOM непосредственно при каждом рендеринге, что может быть полезно в определенных сценариях. Если вы хотите, чтобы ссылка обратного вызова вызывалась только при начальном рендеринге, вы можете обернуть ее хуком useCallback:

import React, { useCallback, useRef } from 'react';

const TextInput = () => {
  const inputRef = useRef(null);
  const handleFocus = () => {
    inputRef.current.focus();
  };
  const setRef = useCallback(node => {
    inputRef.current = node;
  }, []);
  return (
    <div>
      <input type="text" ref={setRef} />
      <button onClick={handleFocus}>Focus Input</button>
    </div>
  );
};

В этом обновленном примере мы заключаем обратный вызов setRef в хук useCallback и предоставляем пустой массив зависимостей []. Это гарантирует, что обратный вызов создается только один раз, во время первоначального рендеринга. Значение inputRef.current будет сохраняться при рендеринге без необходимости дополнительных зависимостей.

Стоит отметить, что вы можете сделать массив зависимостей более конкретным, если хотите, чтобы функция обратного вызова вызывалась только при изменении определенных зависимостей вместе с начальным рендерингом.

Refs для операций чтения/записи

До сих пор мы в основном фокусировались на использовании ссылок для чтения значений из узлов DOM. Однако ссылки также можно использовать для операций записи, что позволяет вам изменять узлы DOM, на которые ссылаются.

Давайте рассмотрим пример, в котором мы используем ссылку для применения пользовательского стиля к элементу DOM:

import React, { useRef } from 'react';

const ColoredText = () => {
  const textRef = useRef(null);
  const handleColorChange = () => {
    textRef.current.style.color = 'red';
  };
  return (
    <div>
      <p ref={textRef}>This is a paragraph</p>
      <button onClick={handleColorChange}>Change Color</button>
    </div>
  );
};

В этом примере мы создаем ссылку с именем textRef, используя хук useRef, и назначаем его атрибуту ref элемента <p>. При нажатии кнопки запускается функция handleColorChange, которая обращается к узлу DOM через textRef.current и изменяет его свойство style.color, чтобы изменить цвет текста на красный.

Важно отметить, что прямое манипулирование DOM таким образом противоречит декларативной природе React. В большинстве случаев рекомендуется использовать управление состоянием React (например, хук useState) для контроля и обновления стилей или других атрибутов. Однако могут быть редкие случаи, когда прямое управление DOM через ссылки может быть полезным для повышения производительности или при взаимодействии со сторонними библиотеками, которые полагаются на императивное управление DOM.

Хотя можно управлять состоянием с помощью ссылок, как показано в предыдущем примере, обычно это не рекомендуется. React предоставляет мощные механизмы управления состоянием, такие как useState Hook, которые предназначены для эффективной обработки обновлений состояния и запуска повторного рендеринга.

💡 Примечание. Если нескольким компонентам требуется доступ к одной и той же ссылке, вы можете использовать поставщик контекста для управления ссылкой и обернуть ею свои компоненты. Используя Bit, вы можете экспортировать/опубликовать провайдера, упрощая совместное использование в разных проектах.

Узнайте больше здесь:



Заключение

В этом всеобъемлющем руководстве мы исследовали мир ссылок React. Мы начали с понимания концепции ссылок как ссылок либо на элементы DOM, либо на изменяемые значения. Мы научились использовать хук useRef для создания и управления ссылками в функциональных компонентах. Мы увидели, как ссылки могут служить переменными экземпляра для отслеживания состояния без запуска повторной визуализации.

Кроме того, мы углубились во взаимодействие между ссылками и DOM, где мы использовали ссылки для императивного доступа и управления элементами HTML. Мы рассмотрели такие сценарии, как чтение значений из узлов DOM, обновление стилей и выполнение других операций чтения/записи.

Мы также изучили альтернативный подход с использованием ссылок обратного вызова, которые обеспечивают прямой доступ к узлу DOM при каждом рендеринге. Мы увидели, как оптимизировать ссылки обратного вызова с помощью хука useCallback, чтобы предотвратить ненужное повторное создание функции обратного вызова.

Наконец, мы подчеркнули важность правильного использования ссылок и предостерегли от чрезмерной зависимости от императивных манипуляций с DOM. Декларативную природу React и его механизмы управления состоянием следует использовать всякий раз, когда это возможно, чтобы обеспечить предсказуемость и удобство сопровождения кода.

Продолжайте исследовать, экспериментировать и создавать потрясающие приложения с помощью React!

Создавайте приложения с повторно используемыми компонентами, как Lego

Инструмент с открытым исходным кодом Bit помогает более чем 250 000 разработчиков создавать приложения с компонентами.

Превратите любой пользовательский интерфейс, функцию или страницу в компонент многократного использования — и поделитесь им со своими приложениями. Легче сотрудничать и строить быстрее.

Подробнее

Разделите приложения на компоненты, чтобы упростить разработку приложений, и наслаждайтесь наилучшими возможностями для рабочих процессов, которые вы хотите:

Микро-интерфейсы

Система дизайна

Совместное использование кода и повторное использование

Монорепо

Узнать больше: