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

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

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

Возьмем небольшой пример.

function UpdateDomTitleAfterFirstRender() {
  /* * do something here, this will happen before
   * the component is rendered.
   */
  useEffect(() => {
    document.title = "From Use Effect";
  }, []);
  return (<div>Hello React!</div>);
}

Чтобы понять, как работает useEffect, важно понять последовательность выполнения:

  • Как только эта функция начнет выполняться, т.е. когда этот компонент будет внедрен на страницу, первой строкой тела функции будет (сделать что-то). Как и любая нормальная функция.
  • Следующей в последовательности является функция useEffect. Но если приглядеться, функция useEffect принимает два параметра:
  • Функция обратного вызова, функция, которую вы хотите выполнить.
  • Массив (который в данном случае пуст, я вернусь к этому позже).
  • Компонент возвращает JSX, который преобразуется в HTML и внедряется в DOM/VDOM.

Обратный звонок

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

// this is the typical setTimeout example that you must have come
// across at least once in your JS programming career. 
console.log("A");
console.log("B");
setTimeout(function() {
  console.log("After 2 seconds printing C");
}, 2000);
console.log("D");
// output
A
B
D
After 2 seconds printing C        // after 2000 ms

setTimeout ожидает минимум заданное количество времени, оно может увеличиваться в зависимости от стеков вызовов, но не меньше. Дело в том, что setTimeout определяет, когда освободить вашу функцию callback при выполнении.

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

Массив, также известный как список зависимостей

Одна из ключевых концепций React заключается в том, что он повторно отображает компонент при обновлении состояния компонента. Итак, вы не станете утверждать, что если каким-то образом нам удастся обновить состояние состояния, используя хуки useState внутри callback, переданного в useEffect, будет бесконечный повторный рендеринг, учитывая, что callback выполняется после рендеринга с учетом:

Что значит,

render => обновить состояние через useEffect => render => обновить состояние через useEffect

Попробуйте запустить следующий код и с отладчиком, установленным внутри функции,

let index = 0;
function InfiniteRender() {
 const [tempVal, updateTempVal] = useState(0);
 useEffect(() => {
   updateTempVal(++index);
 });
 // notice the second argument has not been
 // passed to useEffect.
 return <div>Temp Val {tempVal} </div>;
}

Посмотреть в действии: https://codesandbox.io/s/tender-shaw-rw15q

Примечание. Если вы не знаете, как работает useState, прочитайте этот пост.

Обычно это не желаемое. Более реальный пример:

  • Мы хотим показать некоторые данные, получив их с сервера.
  • После получения ответа от сервера обновите состояние полученными данными, чтобы выполнить повторный рендеринг для обновления ваших заполнителей извлеченными данными.

Итак, как нам остановить этот бесконечный повторный рендеринг?

Ответ кроется во втором аргументе. Массив, переданный в useEffect, также известный как список зависимостей [список, таким образом, массив]. Все, что мы добавляем в список массивов, передается useEffect. Он проверяет и сравнивает предыдущее значение с текущим значением данной переменной в списке. Если они не изменяются, useEffect отменяет эффект и не выполняет функцию callback.

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

Идите вперед и измените код и добавьте пустой массив [] в качестве второго аргумента useEffect. Кроме того, попробуйте добавить tempVal в массив зависимостей и посмотрите, что произойдет.

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

Ключевые моменты:

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

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

компонентWillMount и компонентWillUnmount

componentWillMount очень прост. Нам просто нужно выполнить код, который мы хотим, до того, как функциональный компонент вернет JSX, то есть в любом месте до оператора return.

КомпонентWillUnmount немного отличается, но он прост. Нам нужно вернуть функцию из функции обратного вызова, которую мы передаем в useEffect, и она будет выполнена после размонтирования компонента. См. пример ниже.

.
.
.
useEffect(() => {
  document.addEventListener("click", someCallback);
  // this returned function will be executed
  // when the component will unmount/destroyed
  
  return () => {
    document.removeListener("click", someCallback);
  }
}, []);
.
.
.

Первоначально опубликовано на http://jayendra.co.in 17 июня 2019 г.