Перейти к содержимому

Для чего нужен useeffect

  • автор:

Продвинутые хуки в React: всё о UseEffect

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

  • при визуализации компонента (метод componentDidMount в классовом компоненте);
  • при обновлении компонента (метод componentDidUpdated в классовом компоненте);
  • при удалении компонента из DOM (метод componentWillUnmount в классовом компоненте).

Несколько побочных явлений:

  • Получение данных;
  • Прямое обновление DOM;
  • Установка заголовка страницы;
  • Работа с setInterval или setTimeout;
  • Измерение ширины, высоты или положения элементов в DOM;
  • Установка или получение значений в локальном хранилище.
  • Подписка на услуги и её отмена

Массив зависимостей useEffect

useEffect принимает два параметра. Первый аргумент — это функция обратного вызова, для которой мы будем выполнять побочные эффекты; другой – массив зависимостей. Второй аргумент является необязательным.

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

function MyComponent() < useEffect(() =>< // The side effect will run after every render >) >

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

function MyComponent() < useEffect(() =>< // This side effect will only run once, after the first render >, []) >

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

import < useEffect, useState >from 'react' function MyComponent(< prop >) < const [state, setState] = useState('') useEffect(() =>< // the side effect will only run when the props or state changed >, [prop, state]) >

Стоит отметить, что useEffect использует поверхностное (shallow) сравнение значений зависимостей.

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

Функция очистки useEffect

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

function MyComponent() < useEffect(() => < // this side effect will run after every render return () => < // this side effect will run before the component is unmounted >>) >

Пример реального использования

import < useEffect >from "react" const Modal = (< modalContent, closeModal >) => < useEffect(() => < let timeout = setTimeout(() =>closeModal(), 3000) return () => clearTimeout(timeout) >) return ( ) > export default Modal

Что такое бесконечный цикл в useEffect?

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

  1. Если не указан массив зависимостей:
function App() < const [users, setUsers] = useState([]) useEffect(() => < const getUsers = async () => < const = await axios.get("/api/user") setUsers(data) > getUsers() >) // without dependency array >

Проблема и её решение

Пользовательское значение (состояние) изменяется при визуализации компонента. Поскольку состояние изменилось, компонент визуализируется. Поскольку мы не указали массив зависимостей, useEffect снова запускается, и состояние снова меняется.

function App() < const [users, setUsers] = useState([]) useEffect(() => < const getUsers = async () => < const = await axios.get("/api/user") setUsers(data) > getUsers() >, []) // empty dependency array >
  1. Если в массиве зависимостей указана функция:
function App() < const [count, setCount] = useState(0) function getResult() < return 2 * 2 >useEffect(() => < setCount((count) =>count + 1) >, [getResult]) // we have specified a function in the dependency array return ( 

value of count:

) > export default App

Проблема и её решение

Мы знаем, что useEffect проводит поверхностные сравнения. Это делается, чтобы проверить, были ли обновлены зависимости. При использовании setCount состояние обновляется при первой визуализации компонента.

Как только состояние обновлено, компонент визуализируется снова. Поскольку getResult — это функция, контрольное значение в памяти воссоздается каждый раз при визуализации компонента, поэтому результат поверхностного сравнения возвращает false. Таким образом, образуется бесконечный цикл.

function App() < const [count, setCount] = useState(0) const getResult = useCallback(() =>< return 2 * 2 >, []) useEffect(() => < setCount((count) =>count + 1) >, [getResult]) // we have specified a function in the dependency array return ( 

value of count:

) > export default App

При использовании useCallback функция getResult запоминается. Это гарантирует, что контрольное значение функции getResult не изменится. Когда useEffect выполняет поверхностное сравнение, он возвращает true, и компонент не визуализируется.

Отметим, что существует множество способов избежать бесконечных циклов в компоненте. В статье рассказали только о нескольких.

Использование функций Async-Await в useEffect

Если мы хотим получать данные с помощью API, нам нужно выполнять асинхронные операции.

Как нам это делать с помощью useEffect?

  1. Создайте асинхронную функцию вне useEffect и вызовите ее в useEffect.
const getPosts = async () => < const = await axios.get('api/posts') setPosts(data) > useEffect(() => < getUsers() >, [])
  1. Создайте асинхронную функцию в useEffect и вызовите ее в useEffect.
useEffect(() => < const getPosts = async () => < const = await axios.get('api/posts') setPosts(data) > getUsers() >, [])
  1. Используйте IIFE (функция-выражение, вызываемая сразу после создания) в useEffect.
useEffect(() => < (async () => < const = await axios.get('api/posts') setPosts(data) >)() >, [])

А вот так, делать не надо!

useEffect( async () => < const = await axios.get('api/posts') setPosts(data) >, [])

Советы и рекомендации по эффективному использованию useEffect

Давайте посмотрим на некоторые приемы, которые мы можем использовать в useEffect.

  1. Используйте UseEffect на верхнем уровне.
if(a > b) < useEffect(() =>< // incorrect usage >, []) > useEffect(() => < if(a >b) < // incorrect usage >>, []) useEffect(() => < if(a < b) return // correct usage >, [])

Не нужно использовать useEffect в условных выражениях, циклах и вложенных функциях.

  1. Используйте useEffect для одной задачи.

Выполняйте только одну задачу с использованием useEffect. Для нескольких задач вы можете использовать несколько useEffect для одного и того же компонента. Разделите работу на части и назначьте useEffect для каждой. Назначение useEffect на короткие и одноцелевые функции предотвращает нежелательную повторную визуализацию и позволяет сохранить код чистым и читабельным.

Заключение

useEffect — очень полезный и широко используемый хук React, который стоит освоить. Когда вы привыкнете к нему, вам захочется использовать его постоянно.

Делитесь своим опытом в комментариях.

Для чего нужен useeffect

Хук useEffect позволяет управлять различными сопутствующими действиями в функциональном компоненте или то, что называется «side effects» (побочные эффекты), например, извлечение данных, ручное изменение структуры DOM, использование таймеров, логгирование и т.д.. То есть в useEffect выполняет те действия, которые мы не можем выполнить в основной части функционального компонента. Этот хук фактически служит той же цели, что методы жизненного цикла componentDidMount, componentDidUpdate и componentWillUnmount в классах-компонентах.

В качестве параметра в useEffect() передается функция. При вызове хука useEffect по сути определяется «эффект», который затем применяется в приложении. Когда именно применяется? По умолчанию React применяет эффект после каждого рендеринга, в том числе при первом рендеринге приложения. Причем поскольку подобные эффекты определены внутри компонента, они имеют доступ к объекту props и к состоянию компонента.

Например, изменение структуры DOM через useEffect :

    METANIT.COM        

Здесь мы определяем эффект, который изменяет заголовок страницы. Причем в заголовок выводится значение переменной состояния — переменной name . То есть при загрузке страницы мы увидим в ее заголовке «Привет Tom».

Однако поскольку при вводе в текстовое поле мы изменяем значение в переменной name, и соответственно React будет выполнять перерендеринг приложения, то одновременно с этим будет изменяться и заголовок страницы:

Хук useEffect и изменение DOM в React

Ограничение применения эффекта

По умолчанию эффект выполняется при каждом повторном рендеринге на веб-странице, однако мы можем указать, чтобы React не применял эффект, если определенные значения не изменились между с момента последнего рендеринга. Для этого в useEffect в качестве необязательного параметра передается массив аргументов.

Сначала рассмотрим саму проблему применения эффекта. Допустим у нас есть следующая страница:

    METANIT.COM        

В данном случае в компоненте определены две переменных состояния: name и age. При этом эффект использует только переменную name. Однако даже если переменная name останется без изменений, но переменная age будет изменена, в этом случае эффект будет повторно срабатывать:

useEffect в React и ограничение

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

    METANIT.COM        

Если мы хотим, чтобы эффект вызывался только один раз при самом первом рендеринге, то в качестве параметра передаются пустые квадратные скобки — [] .

React.useEffect(() => < // Изменяем заголовок html-страницы document.title = `Привет $`; console.log("useEffect"); >, []); // эффект срабатывает только один раз - при самом первом рендеринге

Очистка ресурсов

Нередко в приложении возникает необходимость подисывается на различные ресурсы, а после окончания работы и отписываться от них. В этом случае мы можем использовать специальную форму хука useEffect() :

useEffect(() => < // код подписки на ресурс return () =>< // код отписки от ресурса >; >);

В начале функции хука идет подписка на ресурс, а далее оператор return возвращает функцию, которая выполняет отписку от ресурса.

В качестве примера используем подписку/отписку на событие кнопки:

    METANIT.COM         

Итак, на странице определеная кнопка с id=unmountBtn , на событие которой мы будем подписываться. В качестве действия, которое будет выполнять кнопка, в компоненте User определена функция unmount() , которая удаляет данный компонент с веб-страницы:

root.unmount();

В хуке useEffect сначала подписываемся на событие «click» кнопки (то есть на событие нажатия):

unmountBtn.addEventListener("click", unmount);

Оператор return возвращает функцию, которая выполняет отписку от события:

unmountBtn.removeEventListener("click", unmount);

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

React.useEffect(() => < //. return()=> < //. >>, []); // пустой массив - хук выполняется один раз

То есть получится следущее: при загрузке компонент подисывается на событие кнопки. По нажатию на кнопку удаляется компонент. При удалении в хуке происходит удаление подписки — она нам больше не нужна, так как компонент уже удален.

Хук useEffect

useEffect – это один из хуков (специальных функций), который для функциональных компонент заменяет в совокупности такие методы жизненного цикла классовых компонент, как componentDidMount (вызывается после монтирования компоненты), componentDidUpdate (после ее обновления) и componentWillUnmount (после удаления). С помощью useEffect , мы можем вызывать разные побочные эффекты после того, как компонент отрендерится. Некоторым из этих эффектов нужен сброс (прописывается return внутри useEffect), поэтому они возвращают соответствующую функцию.

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

Применение

1. Импортируем из реакт, как и другие хуки:

import React, < useState, useEffect >from 'react';

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

const ProfileStatusWithHooks = (props) => < ///. здесь "разкукоживаем" наш useState, в котором хранится status . useEffect( () =>< setStatus(props.status) //меняем значение в useState >) . return (. )

Теперь сразу отрендерится компонента, а потом useEffect перерисует ее с полученными данными. В таком виде это будет происходить каждый раз после монтирования компоненты. Но в данном случае нам это не подойдет, так как при попытке изменить статус при введении буквы компонента будет повторно рендериться и сразу же перерисовываться этим хуком. В результате мы не сможем “переперенабрать” текст статуса в инпуте, так как тот будет все время перезагружаться.

3. Выходит, что нам нужно применить useEffect только после первой загрузки компоненты. Сделать это можно, указав вторым параметром пустой массив:

useEffect( () => < setStatus(props.status) >, [])

В итоге useEffect сработает раз и не будет мешать потом вводить новый статус в нашу redux-форму (в примере ее не показано). Но использовать такой пустой массив в этом случае считается неправильно, мы как-будто здесь обманываем Реакт. Подробнее об этом рассказывает Ден Абрамов в своей статье useEffect. Это как буд-то мы что-то не договариваем Реакту. Нам желательно указать какой-то конкретный параметр вместо пустого массива для подписки на его изменения.

4. Поэтому в таком простом примере нам можно просто указать в качестве подписки наш props.status. Здесь это сработает идеально:

useEffect( () => < setStatus(props.status) >, [props.status])

Второй пример “из учебника”

Но в более сложных ситуациях, например, когда для подписки будет использовано значение счетчика (count), SetInterval будет при каждом изменении count очищаться и запускаться снова. Вероятно, нас это не устроит. В таком случае, как говорит в статье Д. Абрамов, можно применить такую стратегию, как сделать эффект самодостаточным (т.е. он сам меняет значение, при изменении которого перерисовывается):

    до применения этой стратегии:

useEffect(() => < const =>< setCount(count + 1); >, 1000); return () => clearInterval(id); >, [count]);
useEffect(() => < const => < setCount(c =>c + 1); >, 1000); return () => clearInterval(id); >, []);

В итоге у нас теперь не перерисовывается SetInterval постоянно. Посмотреть этот пример в работе можно здесь. Интересно, что кажется в данном случае Д. Абрамов не отзывается плохо об использовании пустого массива в качестве второго параметра.

ReactJS | Что значит useEffect и как его использовать?

Я написал лёгкую программу, которая использует useState и useEffect , это просто счётчик времени, который сам себя обновляет каждую секунду. Но я не очень хорошо понимаю зачем нужен useEffect . И даже так, я хотел попробовать написать такой счётчик времени. Я использовал документацию React, и дополнил его этими use. . Объясните пожалуйста значение useEffect и уместно ли использовать его в моём коде?

import React, from 'react' const Clock = () => < const [date, setDate] = useState(new Date().toLocaleTimeString()); useEffect(() => < setTimeout(() =>< setDate(new Date().toLocaleTimeString()) >, 1000) >) return ( 

Привет, мир!

Сейчас
) > export default Clock;

(Код работает как надо)
Отслеживать
задан 10 апр 2021 в 15:41
439 2 2 серебряных знака 12 12 бронзовых знаков

2 ответа 2

Сортировка: Сброс на вариант по умолчанию

Использовать useEffect стоит, например, для запросов:

function EffectExample() < const [data, setData] = useState(<>); async function takeData() < const data = await fetch('какой-нибудь url'); setData(data); >useEffect(() => < takeData(); >, []); return > 

Здесь, если вызывать takeData без useEffect, произойдет либо бесконечное количество запросов к серверу, либо infinite loop error , так как обновление состояния data будет рендерить компонент заново, что в свою очередь заново вызывет функцию takeData , которая вновь обновит data и так далее до бесконечности. С useEffect же takedData вызывается только один раз после после первого рендера.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *