В этой статье мы обсудим некоторые распространенные варианты использования хука useRef React.

Начиная

Мы будем использовать Profiler из React Dev Tools, чтобы увидеть, как наши компоненты отображаются. Если у вас нет React Dev Tools и вы планируете продолжить, вам нужно будет сделать паузу и загрузить его сейчас.

Если вы хотите продолжить в своей локальной среде IDE, вы можете найти репозиторий GitHub здесь.

  • fork and clone
  • cd client
  • npm i
  • npm start

Стартовый код

Как всегда, мы начнем с экскурсии по нашей кодовой базе. На этот раз App.js представляет собой смоделированную форму входа.

У нас есть initialFormValues, объект с пустым адресом электронной почты и паролем, который мы используем для инициализации нашего состояния formValues.

Затем у нас есть стандартные функции handleChange и handleSubmit, а handleSubmit регистрирует formValues в консоли.

Наконец, наш JSX отображает форму и назначает соответствующие атрибуты onChange и onSubmit.

В чем проблема?

Поэтому вы можете спросить: «Что не так с этим компонентом? Он работает так, как ожидалось, и я писал подобные формы бессчетное количество раз».

Я тоже, друг. И я тоже.

Откройте свои React Dev Tools и щелкните шестеренку настроек. Затем перейдите на вкладку «Профилировщик» и отметьте параметр «Записывать, почему каждый компонент отображается во время профилирования».

Теперь откройте вкладку Profiler, и вы увидите синюю точку в верхнем левом углу. Щелкните ее, чтобы начать профилирование. Затем введите входные данные и щелкните (теперь красную) точку, чтобы остановить профилирование.

Щелкните компонент приложения слева на вкладке Profiler, и вы увидите список всех рендеров. Обратите внимание, что Profiler предоставляет одну и ту же причину для каждого:

Почему это отобразилось?

  • Крючок 1 изменен

Компонент повторно отображается при каждом нажатии клавиши.

Минимизация рендеринга

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

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

Введите useRef

В самом широком смысле хук useRef создает изменяемую переменную, которая сохраняется между рендерами. Он предоставляет возможность хранить значение, к которому мы можем получить доступ вне цикла рендеринга.

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

Мы начнем с импорта useRef в наш текущий импорт.

Мы инициализируем renders с помощью useRef и установим его в 0. Когда мы инициализируем ref, хук создает объект со свойством current. current — это свойство, которое мы должны использовать для обновления значения ref.

Мы будем использовать хук useEffect без массива зависимостей для увеличения renders каждый раз при рендеринге компонента. Обратите внимание, что мы меняем значение ref, в отличие от useState, мы можем сделать это напрямую.

Давайте визуализируем renders в нашем JSX.

Когда мы вводим ввод, мы видим, что наш счетчик рендеринга увеличивается в реальном времени. Аккуратно, я думаю, но не особенно полезно.

Из документов React:

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

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

…информация, которая не влияет на визуальный вывод вашего компонента.

Что-то вроде формы входа?

Рефакторинг нашей формы с помощью useRef

Давайте начнем с создания двух новых refs для хранения наших значений email и password и инициализации их как null.

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

Далее нам нужно реорганизовать нашу функцию handleSubmit. Вместо регистрации formValues мы создадим объект, установив значения в качестве каждого свойства current ref.

Введите входные данные и нажмите Login. Вы должны увидеть, что ваш объект зарегистрирован правильно, в то время как счетчик рендеринга остается 0. Профилировщик также не находит активности. Изменение значения нашего refs не привело к повторному рендерингу компонента.

С этой работой нам больше не нужно следующее:

  • адрес электронной почты onChange атрибут
  • пароль onChange атрибут
  • handleChange функция
  • formValues состояние
  • initialFormValues объект
  • useState импорт

Хотя мы больше не используем его напрямую, мы должны сохранить атрибуты name для доступности.

Протестируйте приложение еще раз. Мы удалили много кода, но он по-прежнему работает так, как ожидалось!

useRef vs. useState

Это точно демонстрирует точку зрения, поднятую в React Docs: обновление useRef не вызывает повторный рендеринг, что, вероятно, является наиболее существенной разницей между useRef и useState.

Кроме того, вы заметите, что когда мы обновили свойство current в нашем ref, мы сделали это напрямую. Никогда не делайте этого с useState. Вместо этого вы всегда должны использовать функцию установки, если хотите, чтобы ваш пользовательский интерфейс реагировал на изменение. О таком поведении можно прочитать здесь.

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

Последний вариант использования

Прежде чем закончить эту статью, я хотел бы остановиться на том, что, по моему мнению, является самым простым распространенным вариантом использования useRef. Давайте начнем с перемещения нашего счетчика рендеринга ниже тега section, содержащего нашу форму. Мы поместим его в отдельный тег раздела.

Далее мы создадим formRef для нашей формы и назначим его соответствующим образом. Давайте добавим кнопку с текстом Scroll to Render Count.

Затем мы инициализируем renderCountSectionRef, назначим его нашему контейнеру счетчика рендеринга и добавим кнопку с текстом Scroll to Form.

Давайте создадим новую функцию с именем scrollToElement, которая ожидает ref в качестве параметра и прокручивает нас до указанного ref.

Теперь мы установим для свойства onClick значение scrollToElement с соответствующим ref в качестве аргумента.

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

Теперь, когда мы нажимаем кнопки, мы прокручиваем страницу! Это очень похоже на кнопку Scroll To Top или Jump to Recipe, не так ли?

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

Предостережение

После осознания того, что мы можем влиять на изменения элементов DOM напрямую с помощью useRef, может возникнуть соблазн использовать этот метод как аналог querySelector или getElementBy—.

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

Выводы

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

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

Гитхаб | Твиттер | ЛинкедИн | "Веб-сайт"

Ресурсы