Предотвращение повторных рендеров с помощью useRef

Бывают случаи, когда вы не хотите вызывать рендеринг при получении данных от пользователя. useState, на сегодняшний день, является хорошо известным и удобным хуком, поскольку он был реализован в React 16.8. Когда мы устанавливаем переменную состояния с помощью useState, это вызывает рендеринг вашего компонента. Когда мы используем useRef для постоянного хранения информации, это не вызывает рендеринга.

Если вы хотите посмотреть исходный код: https://github.com/BrettThurs10/useRefVersuseState.

Если вы хотите следить за развитием событий в браузере:
https://brettthurs10.github.io/useRef-vs-useState/

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

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

Перейдите к файлу RefComponent.tsx и следуйте за ним:

Установите stage состояние

Чтобы сделать ref-компонент, просто импортируйте его и объявите как переменную:

import {useRef} from React;
...
  const dataRef = useRef("🥧");
  const inputRef = useRef<HTMLInputElement>(null);
  const timesRendered = useRef(0);
  const [inputString, setInputString] = useState("🍕");
...
}
export default RefComponent
Войти в полноэкранный режим Выйти из полноэкранного режима

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

Обновление константы dataRef

После объявления константы dataRef вы можете обновить ее, присвоив значение свойству ‘current’. Это может быть любой примитивный тип, объект или функция.

В моем методе updateDataRef() я делаю именно это.

const updateDataRef = (e: ChangeEvent<HTMLInputElement>) => {
    dataRef.current = e.target.value;
    console.log(dataRef.current);
  };
Вход в полноэкранный режим Выход из полноэкранного режима

Затем я беру первый элемент ввода и устанавливаю атрибут onChange на этот updateDataRef. Теперь, когда бы мы ни вводили текст, он будет принимать значение и обновлять ссылку для нас.

Мачо Ман Халк Хогам GIF — Найти & Поделиться на GIPHY

Найдите и поделитесь этим анимированным GIF со всеми, кого вы знаете. GIPHY — это способ поиска, обмена, обнаружения и создания GIF.

giphy.com

Я также делаю метод handleOnChange() для обновления переменной состояния stringInput.

  const handleOnChange = (e: ChangeEvent<HTMLInputElement>) => {
    setInputString(e.target.value);
  };
Вход в полноэкранный режим Выход из полноэкранного режима

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

Отслеживание изменений состояния

Я сделал метод whereFromMsg(), чтобы отслеживать, из какого блока кода useEffect происходит рендеринг. Я поместил его в два useEffect, которые слушают изменения переменных dataRef и inputString.

  useEffect(() => {
    updateTimesRendered();
    renderMsg("dataRef useEffect");
    whereFromMsg("dataRef", dataRef.current);
  }, [dataRef]);

  useEffect(() => {
    updateTimesRendered();
    renderMsg("inputString useEffect");
    whereFromMsg("inputString", inputString);
    // uncomment to see how useRef can capture the previous state, but not current. i.e. typing in dog in the useState input you will see 'dog' and in the useRef value you will see 'do'
    // dataRef.current = inputString;
  }, [inputString]);
Вход в полноэкранный режим Выход из полноэкранного режима

Когда они это сделают, я вызову 3 метода:

  • updateTimesRendered
  • renderMsg
  • whereFrom
 const updateTimesRendered = () =>
    (timesRendered.current = timesRendered.current + 1);

  const renderMsg = (fromWhere: string) => {
    console.log(
      `✨ Component has rendered ${timesRendered.current} times and most recently from ${fromWhere}`
    );
  };

  const whereFromMsg = (type: string, value: string) => {
    console.log(`${type} === ${value}`);
  };

Войти в полноэкранный режим Выйти из полноэкранного режима

Теперь мы можем посмотреть, что происходит в консоли.

Всякий раз, когда мы вводим данные в любой из входов, мы видим сообщение в консоли.

Обратите внимание, что когда вы вводите данные на входе dataRef, отображается только значение dataRef.current. Нет никакого сообщения о том, что это вызвало рендеринг. Также обратите внимание, что на скриншоте выше значение dataRef в пользовательском интерфейсе все еще установлено на эмодзи пиццы. Это потому, что компонент еще не отрендерился. При последующем рендеринге он обновится с «пицца-эмодзи» на «скейтборд».

Введите второй ввод, и вы увидите, как произойдет транзакция.

Когда мы вводим ввод inputString, мы видим сообщение о том, что он отрисовался, и счетчик отрисовки увеличивается.

Поддерживайте синхронизацию

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

Вы можете увидеть предыдущее состояние переменной dataRef, откомментировав строку dataRef.current = inputString, как показано ниже:

useEffect(() => {
    updateTimesRendered();
    renderMsg("inputString useEffect");
    whereFromMsg("inputString", inputString);
    // uncomment to see how useRef can capture the previous state, but not current. i.e. typing in dog in the useState input you will see 'dog' and in the useRef value you will see 'do'
    // dataRef.current = inputString;
  }, [inputString]);
Вход в полноэкранный режим Выход из полноэкранного режима

Теперь, когда мы вводим данные во второй ввод, мы видим, что оба значения изменились, но значение dataRef не является текущим.

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

Бонусные очки:

Нажатие на кнопку focus inputRef действительно установит фокус на 2-й элемент ввода (обводя его контуром). Это просто показывает, как можно использовать хук useRef и прикрепить его к элементу DOM, чтобы получить к нему прямой доступ.

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

GIF «Привет из фильма Гуфи» — Найти & Поделиться на GIPHY

Найдите и поделитесь этим анимированным GIF со всеми, кого вы знаете. GIPHY — это способ поиска, обмена, обнаружения и создания GIF.

giphy.com

Оцените статью
Procodings.ru
Добавить комментарий