В новейшей версии 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 г.