Useeffect react js для чего
Изучение методов кэширования в React
Как использовать memoization, contexts, useMemo, useState, и useEffect
Для будущих учащихся на курсе «React.js Developer» подготовили перевод материала. Также приглашаем всех желающих на открытый вебинар «ReactJS: быстрый старт. Сильные и слабые стороны».
Сбор данных в React — это одно. Хранение и кэширование этих данных — это другая история. Возможности кажутся бесконечными, а различия часто тонкие, что делает выбор правильной техники иногда немного сложным.
И много анимированных GIF-файлов. Что еще вы можете желать?
Наши данные
Перед тем, как нам углубиться в код, мы можем бегло взглянуть на данные, которые будем извлекать из (большинства) наших компонентов. Файл, который действует как наш API, выглядит следующим образом:
Этот код выполняется, когда мы делаем запрос к пути /api/people в нашем проекте. Как видите, мы вернули объект с двумя свойствами:
randomNumber : случайное число в диапазоне 0-10000.
people : статический массив с тремя вымышленными именами.
Свойство randomNumber поможет нам визуализировать, отображаем ли мы кэшированные данные во фронтенде или нет. Просто продолжайте читать. Скоро это будет понятно.
Компонент People
Когда мы отображаем данные из нашего API, мы передаем их компоненту под названием PeopleRenderer. Это выглядит так:
Учитывая все вышесказанное, давайте посмотрим на первое решение ниже.
useEffect
Внутри наших компонентов мы могли бы использовать хук-эффект useEffect Hook для получения данных. Затем мы можем хранить их локально (внутри компонента), используя useState :
При передаче пустого массива в качестве второго параметра (см. строку 11), useEffect Hook (хук-эффект) будет выполнен, когда наш компонент будет установлен в DOM (Document Object Model) — и только после этого. Он не будет выполняться снова, когда наш компонент будет перезагружен. Это называется «выполнить один раз» Hook (хук).
Ограничение использования useEffect в этом случае заключается в том, что когда мы имеем несколько экземпляров компонента в нашем DOM, все они будут получать данные по отдельности (когда они установлены):
В этом способе нет ничего плохого. Иногда, это именно то, что мы хотим. Но в других случаях нам может понадобиться один раз получить данные и повторно использовать их во всех других случаях путем кэширования. Мы можем использовать несколько способов для достижения этого.
Memoization (Мемоизация)
Memoization — это причудливое слово для очень простой техники. Это означает, что вы создаете функцию, и каждый раз, когда она вызывается заново, она сохраняет результаты выполнения в кэше для предотвращения повторных вычислений.
При первом вызове этой memoized функции результаты рассчитываются (или извлекаются, что бы Вы ни делали внутри тела функции). Перед возвращением результатов вы храните их в кэше под ключом, который создается с входными параметрами:
Создание такого кода шаблона может быстро стать громоздким, поэтому такие популярные библиотеки, как Lodash и Underscore, предоставляют утилиты, которые можно использовать для легкого создания memoized функций:
Использование memoization для получения данных
Теперь мы можем заменить наш useEffect Hook на другой, который выглядит так:
Так как результат getData мемоизуется (memoized), все наши компоненты получат одни и те же данные, когда они будут смонтированы:
Анимация: В наших компонентах используется один и тот же memoized Promise.
Стоит также отметить, что данные уже предварительно собраны, когда мы открываем страницу memoize.tsx (перед тем, как мы смонтируем первую версию нашего компонента). Это потому, что мы определили нашу функцию getData в отдельном файле, который включен в верхнюю часть нашей страницы, и Promise создается при загрузке этого файла.
Мы также можем аннулировать, признать недействительным (пустым) кэш, назначив совершенно новый Cache в качестве свойства cache нашей мемоизованной (memoized) функции:
Как вариант, вы можете очистить существующий кэш (с помощью функции Map):
Но это специфичная функциональность для Lodash. Другие библиотеки требуют иных решений. Здесь вы видите аннулирование кэша в действии на примере:
Анимация: Сброс кэша memoized функции getData.
React Context (React Контекст)
Еще одним популярным и широко обсуждаемым (но часто непонятным) инструментом является React Context. И на заметку, еще раз, он не заменяет такие инструменты, как Redux. Это не инструмент управления состоянием.
Mark Erikson ведет тяжелую битву в Интернете и продолжает объяснять, почему это так. Настоятельно рекомендую прочитать его последнюю статью на эту тему.
И если вам это действительно интересно, прочитайте и мою статью по этой теме:
Так что же такое Context (Контекст)? Это механизм для внесения данных в ваше дерево компонентов. Если у вас есть какие-то данные, вы можете хранить их, например, с помощью useState Hook внутри компонента высоко в иерархии компонентов. Затем вы можете с помощью Context Provider внедрить данные в дерево, после чего вы сможете читать (использовать) данные в любом из компонентов внизу.
Это легче понять на примере. Во-первых, создайте новый контекст:
Затем мы заворачиваем (wrap) тот компонент, который отображает (renders) ваши компоненты People, с помощью Context Provider:
На 12-ой строке мы можем сделать все, что захотим. В какой-то момент, далее вниз по дереву, мы отобразим наш компонент (компоненты) People:
Мы можем использовать значение от Provider с помощью useContext Hook. Результат выглядит следующим образом:
Анимация: Использование данных из Context (Контекста).
Обратите внимание на одну важную разницу! В конце анимации выше мы нажимаем кнопку «Set new seed». При этом данные, которые хранятся в нашем Context Provider, будут заново извлечены. После этого (через 750 мс) вновь полученные данные становятся новым значением нашего Provider, а наши компоненты People перезагружаются. Как видите, все они используют одни и те же данные.
Это большое отличие от примера мемоизации (memoization), который мы рассмотрели выше. В том случае каждый компонент хранил свою копию мемоизуемых (memoized) данных с помощью useState. А в этом случае, используя и потребляя контекст, они не хранят копии, а только используют ссылки на один и тот же объект. Поэтому при обновлении значения в нашем Provider все компоненты обновляются одними и теми же данными.
useMemo
И последнее, но не менее важное, это беглый взгляд на useMemo. Этот Hook отличается от других методов, которые мы уже рассматривали, в том смысле, что это только форма кэширования на локальном уровне: внутри одного элемента компонента. Вы не можете использовать useMemo для обмена данными между несколькими компонентами — по крайней мере, без таких обходных путей, как пробрасывание (prop-drilling) или внедрение зависимостей ((dependency injection) (например, React Context)).
useMemo является инструментом оптимизации. Вы можете использовать его для предотвращения пересчета значения при каждом повторном использовании вашего компонента. Документация описывает это лучше, чем могу я, но давайте рассмотрим пример:
getRnd (строка 2): функция, возвращающая случайное число в диапазоне 0-10000.
Функция выполняется только при изменении значения возраста. Если наш компонент повторно будет вызван (re-rendered) и значение age не изменится, то useMemo просто вернёт мемоизованный (memoized) результат.
В данном примере вычисление pow не очень сложное, но вы можете представить себе преимущества этого, учитывая, что наша функция становится более тяжелой и нам приходится часто визуализировать (re-rendered) наш компонент.
Анимация: Много обращений (re-renders), но значение pow мемоизуется (memoized) с useMemo.
Анимация: Наше мемоизованное (memoized) значение обновляется при обновлении зависимости.
Заключение
Существует множество методик и утилит для кэширования данных в JavaScript. Эта статья дает только поверхностные знания, но я надеюсь, что она содержит некоторые сведения, которые вы захватите с собой на пути своего развития.
Весь программный код, использованный в этой статье, можно найти у меня в архиве на GitLab.
Продвинутые React Hooks: подробный разбор useEffect
С выходом React 16.8 в 2019 году React Hooks наконец-то стали доступны для использования в пригодных для эксплуатации приложениях. Хуки позволяют React-разработчикам делать функциональные компоненты с отслеживанием состояния и не использовать классовые компоненты.
UseEffect — один из трёх больших встроенных React Hooks и один из самых популярных хуков. Он даёт возможность создавать условные изменения, ссылающиеся на состояние программы внутри функционального компонента.
Ближе к концу статьи вы узнаете, как и когда реализовывать этот хук для создания реактивных программ, и поймёте, почему он так часто используется React-разработчиками.
В статье мы рассмотрим следующие вопросы и темы:
Что такое React Hooks?
В React есть функциональные компоненты, которые не содержат внутреннего состояния. А ещё есть классовые компоненты, добавляющие в программу логику с отслеживанием состояния и позволяющие использовать методы жизненного цикла.
Многие разработчики были против такого подхода, так как классовым компонентам для поддержки внутренних состояний требуются классы ES6.
И вот была предложена альтернатива в виде React Hooks.
React Hooks — это функции, которые позволяют подцепиться к состоянию и жизненному циклу React из функциональных компонентов. Это даёт возможность использовать React без классов, которые многим не нравятся из-за их зависимости от вызовов this JavaScript. А главное — хуки включаются по желанию и работают с имеющимся кодом.
Существует несколько встроенных хуков (таких как useEffect или useState ), которые ссылаются на стандартные внутренние состояния. Есть также возможность создавать пользовательские хуки, ссылающиеся на выбранные состояния.
Вот самые популярные встроенные хуки:
Преимущества React Hooks:
Сравнение реализации компонентов с классами и хуками
Хуки предназначены для всего того, на что способны классы, и могут даже больше. Посмотрим, как преобразится старый код на React с использованием хуков вместо классов.
Вот старый код на React без хуков:
Для этого внесём в код следующие изменения:
Вот как теперь выглядит то же самое приложение на React с хуками:
Хуки легко задействовать в приложении, и код при этом становится более удобным для восприятия!
Что представляет собой хук useEffect?
useEffect — один из самых популярных хуков, ведь он выполняет побочные эффекты в функциональных компонентах. Присмотримся к нему повнимательнее, чтобы понять, как это происходит.
Хук useEffect позволяет запускать дополнительный код после того, как React обновит DOM.
Синтаксис
Хук useEffect принимает два аргумента:
Первый аргумент — это функция обратного вызова, которая по умолчанию запускается после каждого отображения.
Второй аргумент — это опциональный массив зависимостей, который указывает хуку на выполнение обратного вызова только при наличии изменения в целевом состоянии. Хук сравнивает значение предыдущего и текущего состояния каждой зависимости. Если эти два значения не совпадают, хук использует обратный вызов первого аргумента.
Массивы зависимостей переопределяют поведение обратного вызова по умолчанию и обеспечивают, что хук проигнорирует всё остальное в области компонента.
Примеры использования
Вот типичные сценарии применения useEffect :
В каждом из этих случаев useEffect используется вместо метода жизненного цикла.
Использование массива зависимостей с хуком useEffect Hook
Для оптимизации хука useEffect необходимо правильно задействовать массивы зависимостей. Важно использовать эти хуки для предотвращения ненужных повторных отображений, даже когда ничего не меняется.
Приведённый ниже код выводит на страницу полученное сообщение, но не использует массив зависимостей.
Всё вроде хорошо, но при открытии консоли браузера обнаруживается, что сообщение >> Loading Message несколько раз перезапускалось.
Сообщение не изменилось, поэтому оптимизируем всё это: сообщения будут загружаться и получаться только раз.
Секрет в добавлении пустого массива зависимостей. Строки 8–10 просто заменяются на:
По умолчанию хук useEffect запускается после каждого повторного отображения. А с массивом зависимостей он запускается один раз и затем запускается снова при каждом изменении передаваемой зависимости. Пустой массив не оставляет условий для повторного запуска хука, обеспечивая получение сообщения только при первом отображении.
Запуск функции useEffect с изменением состояния или пропсов
Массивы зависимостей также полезны при создании адаптивных приложений. Но это должны быть заполненные массивы.
Возьмём приложение на React, позволяющее пользователям устанавливать псевдоним, вводя его в поле ввода. После установки псевдонима приложение получает персонализированное приветственное сообщение из внешнего API.
Вот и всё, мы только что создали метод componentDidUpdate с помощью хуков!
Что дальше?
Мы убедились, что React Hooks — это мощный инструмент, который позволяет обойти многие неудобные элементы старого синтаксиса React.
Справочник API хуков
Хуки — нововведение в React 16.8, которое позволяет использовать состояние и другие возможности React без написания классов.
На этой странице описан API, относящийся к встроенным хукам React.
Если вы новичок в хуках, вы можете сначала ознакомиться с общим обзором. Вы также можете найти полезную информацию в главе «Хуки: ответы на вопросы».
Возвращает значение с состоянием и функцию для его обновления.
Во время первоначального рендеринга возвращаемое состояние ( state ) совпадает со значением, переданным в качестве первого аргумента ( initialState ).
Функция setState используется для обновления состояния. Она принимает новое значение состояния и ставит в очередь повторный рендер компонента.
Кнопки «+» и «-» используют функциональную форму, потому что обновлённое значение основано на предыдущем значении. Но кнопка «Сбросить» использует обычную форму, потому что она всегда устанавливает счётчик обратно в 0.
Если функция обновления возвращает абсолютно такой же результат как и текущее состояние, то последующие повторные рендеры будут полностью пропущены.
Ленивая инициализация состояния
Аргумент initialState — это состояние, используемое во время начального рендеринга. В последующих рендерах это не учитывается. Если начальное состояние является результатом дорогостоящих вычислений, вы можете вместо этого предоставить функцию, которая будет выполняться только при начальном рендеринге:
Досрочное прекращение обновления состояния
Принимает функцию, которая содержит императивный код, возможно, с эффектами.
Мутации, подписки, таймеры, логирование и другие побочные эффекты не допускаются внутри основного тела функционального компонента (называемого этапом рендеринга React). Это приведёт к запутанным ошибкам и несоответствиям в пользовательском интерфейсе.
По умолчанию эффекты запускаются после каждого завершённого рендеринга, но вы можете решить запускать их только при изменении определённых значений.
Функция очистки запускается до удаления компонента из пользовательского интерфейса, чтобы предотвратить утечки памяти. Кроме того, если компонент рендерится несколько раз (как обычно происходит), предыдущий эффект очищается перед выполнением следующего эффекта. В нашем примере это означает, что новая подписка создаётся при каждом обновлении. Чтобы избежать воздействия на каждое обновление, обратитесь к следующему разделу.
Порядок срабатывания эффектов
Хотя useEffect откладывается до тех пор, пока браузер не выполнит отрисовку, он гарантированно срабатывает перед любыми новыми рендерами. React всегда полностью применяет эффекты предыдущего рендера перед началом нового обновления.
Условное срабатывание эффекта
По умолчанию эффекты запускаются после каждого завершённого рендера. Таким образом, эффект всегда пересоздаётся, если значение какой-то из зависимости изменилось.
Если вы хотите использовать эту оптимизацию, обратите внимание на то, чтобы массив включал в себя все значения из области видимости компонента (такие как пропсы и состояние), которые могут изменяться с течением времени, и которые будут использоваться эффектом. В противном случае, ваш код будет ссылаться на устаревшее значение из предыдущих рендеров. Отдельные страницы документации рассказывают о том, как поступить с функциями и что делать с часто изменяющимися массивами.
Если вы хотите запустить эффект и сбросить его только один раз (при монтировании и размонтировании), вы можете передать пустой массив ( [] ) вторым аргументом. React посчитает, что ваш эффект не зависит от каких-либо значений из пропсов или состояния и поэтому не будет выполнять повторных запусков эффекта. Это не обрабатывается как особый случай — он напрямую следует из логики работы входных массивов.
Массив зависимостей не передаётся в качестве аргументов функции эффекта. Тем не менее, в теории вот что происходит: каждое значение, на которое ссылается функция эффекта, должно также появиться в массиве зависимостей. В будущем достаточно продвинутый компилятор сможет создать этот массив автоматически.
Принимает объект контекста (значение, возвращённое из React.createContext ) и возвращает текущее значение контекста для этого контекста. Текущее значение контекста определяется пропом value ближайшего над вызывающим компонентом в дереве.
Запомните, аргументом для useContext должен быть непосредственно сам объект контекста:
useContext(MyContext) позволяет только читать контекст и подписываться на его изменения. Вам всё ещё нужен выше в дереве, чтобы предоставить значение для этого контекста.
Соединим все вместе с Context.Provider
Это пример из раздела Продвинутые темы: Контекст, только переписанный с использованием хуков. В этом же разделе можно найти больше информации о том, как и когда использовать объект Context.
Следующие хуки являются вариантами базовых из предыдущего раздела или необходимы только для конкретных крайних случаев. Их не требуется основательно изучать заранее.
Указание начального состояния
Это позволяет извлечь логику для расчёта начального состояния за пределы редюсера. Это также удобно для сброса состояния позже в ответ на действие:
Досрочное прекращение dispatch
Если вы вернёте то же значение из редюсера хука, что и текущее состояние, React выйдет без перерисовки дочерних элементов или запуска эффектов. (React использует алгоритм сравнения Object.is.)
Передайте встроенный колбэк и массив зависимостей. Хук useCallback вернёт мемоизированную версию колбэка, который изменяется только, если изменяются значения одной из зависимостей. Это полезно при передаче колбэков оптимизированным дочерним компонентам, которые полагаются на равенство ссылок для предотвращения ненужных рендеров (например, shouldComponentUpdate ).
Массив зависимостей не передаётся в качестве аргументов для колбэка. Концептуально, однако, это то, что они представляют: каждое значение, использованное в колбэке, должно также появиться в массиве зависимостей. В будущем достаточно продвинутый компилятор может создать этот массив автоматически.
Передайте «создающую» функцию и массив зависимостей. useMemo будет повторно вычислять мемоизированное значение только тогда, когда значение какой-либо из зависимостей изменилось. Эта оптимизация помогает избежать дорогостоящих вычислений при каждом рендере.
Если массив не был передан, новое значение будет вычисляться при каждом рендере.
Массив зависимостей не передаётся в качестве аргументов функции. Концептуально, однако, это то, что они представляют: каждое значение, на которое ссылается функция, должно также появиться в массиве зависимостей. В будущем достаточно продвинутый компилятор может создать этот массив автоматически.
Обычный случай использования — это доступ к потомку в императивном стиле:
Возможно, вы знакомы с рефами в основном как со способом получить доступ к DOM. Если вы передадите React объект рефа с помощью подобного выражения
Но хук useRef() полезен не только установкой атрибута с рефом. Он удобен для сохранения любого мутируемого значения, по аналогии с тем, как вы используете поля экземпляра в классах.
useDebugValue может использоваться для отображения метки для пользовательских хуков в React DevTools.
Мы не рекомендуем добавлять значения отладки в каждый пользовательский хук. Это наиболее ценно для пользовательских хуков, которые являются частью общих библиотек.
Отложите форматирование значений отладки
В некоторых случаях форматирование значения для отображения может быть дорогой операцией. Это также не нужно, если хук не проверен.
По этой причине useDebugValue принимает функцию форматирования в качестве необязательного второго параметра. Эта функция вызывается только при проверке хуков. Она получает значение отладки в качестве параметра и должна возвращать форматированное отображаемое значение.
Продвинутые React Hooks: подробный разбор useEffect
Feb 22 · 7 min read
С выходом React 16.8 в 2019 году React Hooks наконец-то стали доступны для использования в пригодных для эксплуатации приложениях. Хуки позволяют React-разработчикам делать функциональные компоненты с отслеживанием состояния и не использовать классовые компоненты.
UseEffect — один из трёх больших встроенных React Hooks и один из самых популярных хуков. Он даёт возможность создавать условные изменения, ссылающиеся на состояние программы внутри функционального компонента.
Ближе к концу статьи вы узнаете, как и когда реализовывать этот хук для создания реактивных программ, и поймёте, почему он так часто используется React-разработчиками.
В статье мы рассмотрим следующие вопросы и темы:
Что такое React Hooks?
В React есть фу н кциональные компоненты, которые не содержат внутреннего состояния. А ещё есть классовые компоненты, добавляющие в программу логику с отслеживанием состояния и позволяющие использовать методы жизненного цикла.
Многие разработчики были против такого подхода, так как классовым компонентам для поддержки внутренних состояний требуются классы ES6.
И вот была предложена альтернатива в виде React Hooks.
React Hooks — это функции, которые позволяют подцепиться к состоянию и жизненному циклу React из функциональных компонентов. Это даёт возможность использовать React без классов, которые многим не нравятся из-за их зависимости от вызовов this JavaScript. А главное — хуки включаются по желанию и работают с имеющимся кодом.
Существует несколько встроенных хуков (таких как useEffect или useState ), которые ссылаются на стандартные внутренние состояния. Есть также возможность создавать пользовательские хуки, ссылающиеся на выбранные состояния.
Вот самые популярные встроенные хуки:
Преимущества React Hooks:
Сравнение реализации компонентов с классами и хуками
Хуки предназначены для всего того, на что способны классы, и могут даже больше. Посмотрим, как преобразится старый код на React с использованием хуков вместо классов.
Вот старый код на React без хуков:
Для этого внесём в код следующие изменения:
Вот как теперь выглядит то же самое приложение на React с хуками:
Хуки легко задействовать в приложении, и код при этом становится более удобным для восприятия!
Что представляет собой хук useEffect?
useEffect — один из самых популярных хуков, ведь он выполняет побочные эффекты в функциональных компонентах. Присмотримся к нему повнимательнее, чтобы понять, как это происходит.
Хук useEffect позволяет запускать дополнительный код после того, как React обновит DOM.
Синтаксис
Хук useEffect принимает два аргумента:
Первый аргумент — это функция обратного вызова, которая по умолчанию запускается после каждого отображения.
Второй аргумент — это опциональный массив зависимостей, который указывает хуку на выполнение обратного вызова только при наличии изменения в целевом состоянии. Хук сравнивает значение предыдущего и текущего состояния каждой зависимости. Если эти два значения не совпадают, хук использует обратный вызов первого аргумента.
Массивы зависимостей переопределяют поведение обратного вызова по умолчанию и обеспечивают, что хук проигнорирует всё остальное в области компонента.
Примеры использования
Вот типичные сценарии применения useEffect :
В каждом из этих случаев useEffect используется вместо метода жизненного цикла.
Использование массива зависимостей с хуком useEffect Hook
Для оптимизации хука useEffect необходимо правильно задействовать массивы зависимостей. Важно использовать эти хуки для предотвращения ненужных повторных отображений, даже когда ничего не меняется.
Приведённый ниже код выводит на страницу полученное сообщение, но не использует массив зависимостей.
Всё вроде хорошо, но при открытии консоли браузера обнаруживается, что сообщение >> Loading Message несколько раз перезапускалось.
Сообщение не изменилось, поэтому оптимизируем всё это: сообщения будут загружаться и получаться только раз.
Секрет в добавлении пустого массива зависимостей. Строки 8–10 просто заменяются на:
По умолчанию хук useEffect запускается после каждого повторного отображения. А с массивом зависимостей он запускается один раз и затем запускается снова при каждом изменении передаваемой зависимости. Пустой массив не оставляет условий для повторного запуска хука, обеспечивая получение сообщения только при первом отображении.
Запуск функции useEffect с изменением состояния или пропсов
Массивы зависимостей также полезны при создании адаптивных приложений. Но это должны быть заполненные массивы.
Возьмём приложение на React, позволяющее пользователям устанавливать псевдоним, вводя его в поле ввода. После установки псевдонима приложение получает персонализированное приветственное сообщение из внешнего API.