useRef¶
useRef
- это хук React, позволяющий ссылаться на значение, которое не нужно для рендеринга.
1 |
|
Описание¶
useRef(initialValue)
¶
Вызовите useRef
на верхнем уровне вашего компонента, чтобы объявить реф.
1 2 3 4 5 6 7 |
|
Параметры¶
initialValue
: Значение, которое вы хотите, чтобы свойствоcurrent
объекта ref было первоначальным. Это может быть значение любого типа. Этот аргумент игнорируется после первоначального рендеринга.
Возвращаемое значение¶
Функция useRef
возвращает объект с одним свойством:
current
: Изначально оно установлено наinitialValue
, которое вы передали. Позже вы можете установить его в другое значение. Если вы передадите объект ref в React как атрибутref
узла JSX, React установит его свойствоcurrent
.
При следующем рендере useRef
вернет тот же объект.
Предупреждения¶
- Вы можете изменить свойство
ref.current
. В отличие от состояния, оно является изменяемым. Однако, если оно содержит объект, который используется для рендеринга (например, часть вашего состояния), то не стоит мутировать этот объект. - Когда вы изменяете свойство
ref.current
, React не перерисовывает ваш компонент. React не знает, когда вы изменяете его, потому что ref - это обычный объект JavaScript. - Не записывайте или читайте
ref.current
во время рендеринга, кроме инициализации. Это делает поведение вашего компонента непредсказуемым. - В строгом режиме React будет вызывать функцию вашего компонента дважды, чтобы помочь вам найти случайные примеси. Это поведение только для разработки и не влияет на производство. Каждый объект ref будет создан дважды, но одна из версий будет отброшена. Если ваша функция компонента является чистой (как и должно быть), это не должно повлиять на поведение.
Использование¶
Ссылка на значение с помощью ссылки¶
Вызовите useRef
на верхнем уровне вашего компонента, чтобы объявить одну или более реф-ссылок.
1 2 3 4 5 6 |
|
useRef
возвращает объект ref с одним пропсом current
, первоначально установленным на начальное значение, которое вы указали.
При последующих рендерах useRef
будет возвращать тот же объект. Вы можете изменить его свойство current
, чтобы сохранить информацию и прочитать ее позже. Это может напомнить вам состояние, но есть важное отличие.
Изменение ссылки не вызывает повторного рендеринга. Это означает, что ссылки идеально подходят для хранения информации, которая не влияет на визуальный вывод вашего компонента. Например, если вам нужно хранить ID интервала и получить его позже, вы можете поместить его в ссылку. Чтобы обновить значение внутри ссылки, вам нужно вручную изменить ее current
свойство:
1 2 3 4 5 6 |
|
Позже вы можете прочитать ID этого интервала из реферера, чтобы вызвать функцию очистки таймера:
1 2 3 4 |
|
Используя реф-ссылку, вы гарантируете, что:
- Вы можете сохранять информацию между повторными рендерами (в отличие от обычных переменных, которые сбрасываются при каждом рендере).
- Ее изменение не вызывает повторного рендеринга (в отличие от переменных состояния, которые вызывают повторный рендеринг).
- Информация является локальной для каждой копии вашего компонента (в отличие от внешних переменных, которые являются общими).
Изменение ссылки не вызывает повторного рендеринга, поэтому ссылки не подходят для хранения информации, которую вы хотите вывести на экран. Для этого лучше использовать состояние. Подробнее о выбор между useRef
и useState
Примеры ссылок на значение с помощью useRef¶
1. Счетчик кликов
Этот компонент использует ссылку для отслеживания того, сколько раз была нажата кнопка. Обратите внимание, что здесь можно использовать ссылку вместо state, потому что счетчик кликов считывается и записывается только в обработчике событий.
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Если вы покажете {ref.current}
в JSX, число не будет обновляться по щелчку. Это происходит потому, что установка ref.current
не вызывает повторного рендеринга. Вместо этого информация, которая используется для рендеринга, должна быть состоянием.
2. Секундомер
В этом примере используется комбинация состояния и реф-ссылки. И startTime
и now
являются переменными состояния, потому что они используются для рендеринга. Но нам также нужно хранить ID интервала, чтобы мы могли остановить интервал по нажатию кнопки. Поскольку идентификатор интервала не используется для рендеринга, целесообразно хранить его в ссылке и обновлять вручную.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
|
Рефы во время рендеринга
Не пишите и не читайте ref.current
во время рендеринга..
React ожидает, что тело вашего компонента ведет себя как чистая функция:
- Если входные данные (пропсы, состояние и контекст) одинаковы, оно должно возвращать точно такой же JSX.
- Вызов в другом порядке или с другими аргументами не должен влиять на результаты других вызовов.
Чтение или запись ссылки во время рендеринга нарушает эти ожидания.
1 2 3 4 5 6 7 8 |
|
Вместо этого вы можете читать или записывать ссылки из обработчиков событий или эффектов.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Если вам нужно прочитать или записать что-то во время рендеринга, используйте состояние вместо этого.
Если вы нарушите эти правила, ваш компонент может продолжать работать, но большинство новых функций, которые мы добавляем в React, будут опираться на эти ожидания. Подробнее о соблюдении чистоты компонентов
Манипулирование DOM с помощью ссылки¶
Особенно часто ссылка используется для манипуляций с DOM. React имеет встроенную поддержку для этого.
Сначала объявите реф-ссылку с начальным значением равным null
:
1 2 3 4 5 6 |
|
Затем передайте ваш объект ref
в качестве атрибута ref
в JSX узла DOM, которым вы хотите манипулировать:
1 2 |
|
После того как React создаст DOM-узел и поместит его на экран, React установит свойство current
вашего объекта ref на этот DOM-узел. Теперь вы можете получить доступ к DOM-узлу input
и вызвать такие методы, как focus()
:
1 2 3 |
|
React установит свойство current
обратно в null
, когда узел будет удален с экрана.
Подробнее о манипулирование DOM с помощью ссылок
Примеры манипулирования DOM с useRef¶
1. Фокусировка текстового ввода
В этом примере нажатие на кнопку фокусирует ввод:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
2. Прокрутка изображения в режиме просмотра
В этом примере нажатие на кнопку прокручивает изображение в окне просмотра. Он использует ссылку на DOM-узел list
, а затем вызывает API DOM querySelectorAll
, чтобы найти изображение, которое мы хотим прокрутить.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
|
3. Воспроизведение и приостановка видео
Этот пример использует ссылку для вызова play()
и pause()
на DOM-узле <video>
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
|
4. Выставление ссылки на собственный компонент
Иногда вы можете захотеть позволить родительскому компоненту манипулировать DOM внутри вашего компонента. Например, вы пишете компонент MyInput
, но хотите, чтобы родительский компонент мог фокусироваться на вводе (к которому родительский компонент не имеет доступа). Вы можете использовать комбинацию useRef
для хранения ввода и forwardRef
для передачи его родительскому компоненту. Читайте подробное руководство здесь.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
Избегание воссоздания содержимого ссылки¶
React сохраняет начальное значение ссылки один раз и игнорирует его при последующих рендерах.
1 2 3 4 |
|
Хотя результат new VideoPlayer()
используется только для начального рендеринга, вы все равно вызываете эту функцию при каждом рендеринге. Это может быть расточительно, если создаются дорогие объекты.
Чтобы решить эту проблему, вы можете инициализировать ссылку следующим образом:
1 2 3 4 5 6 7 |
|
Обычно запись или чтение ref.current
во время рендеринга не допускается. Однако в данном случае это нормально, потому что результат всегда один и тот же, а условие выполняется только во время инициализации, поэтому оно полностью предсказуемо.
Как избежать проверки нуля при инициализации useRef
позже
Если вы используете программу проверки типов и не хотите всегда проверять null
, вы можете попробовать такой шаблон:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Здесь сам playerRef
является нулевым. Однако вы должны быть в состоянии убедить вашу программу проверки типов, что не существует случая, когда getPlayer()
возвращает null
. Затем используйте getPlayer()
в обработчиках событий.
Устранение неполадок¶
Я не могу получить ссылку на пользовательский компонент¶
Если вы попытаетесь передать ref
своему собственному компоненту следующим образом:
1 2 3 |
|
Вы можете получить ошибку в консоли:
Console
Warning: Function components cannot be given refs. Attempts to access this ref will fail. Did you mean to use React.forwardRef()?
По умолчанию ваши собственные компоненты не передают ссылки на узлы DOM внутри них.
Чтобы исправить это, найдите компонент, к которому вы хотите получить ссылку:
1 2 3 |
|
А затем оберните его в forwardRef
следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Тогда родительский компонент сможет получить ссылку на него.
Подробнее о доступе к узлам DOM другого компонента
Источник — https://react.dev/reference/react/useRef