useEffect «над капотом», життєвий цикл компонента та джуни — не джуни
Так, вам не здалося, це ще одна стаття про useEffect (але далеко не тільки). Вона буде корисна тим, хто лише починає знайомство з React або ще не має глибокого розуміння хуків і достатнього практичного досвіду. Це експериментальний формат, бо в матеріалі я вирішив «вбити двох зайців»: поєднати технічну інформацію з власними спостереженнями та роздумами.
Всім привіт! Мене звати Максим Маслов і вже пішов четвертий рік, як я захоплююся фронтенд-розробкою, постійно вивчаю нові технології, полюбляю брати участь в різних дискусіях, ділитися своїм досвідом та переймати досвід інших розробників.
Нещодавно я подивився одну з публічних співбесід на frontend middle-розробника, яку вів широко відомий дивовижний розробник*.
Один з аргументів кандидата, чому він вирішив, що вже готовий претендувати на позицію «мідла», полягав в тому, що він вже має поглиблене розуміння того, як працюють технології.
Але я зовсім не зрозумів відповідей кандидата, які стосувалися useEffect. В мене склалося таке враження, що людина не розуміє в принципі як він працює, а позиція інтервʼюєра була також не зрозумілою, бо виправлень не було, як і будь-яких коментарів.
В принципі це ОК, тому що це співбесіда, а не лекція. В реальному житті той, хто проводить співбесіду, не повинен тебе навчати, його задача — зрозуміти рівень знань і навичок кандидата.

*Я не хочу прямо вказувати імен: хто, де, коли і що, щоб нікого не демотивувати проходити публічні співбесіди. Помилятися нормально, а знати абсолютно все неможливо. І взагалі, я захоплююсь і надихаюся людьми, які приймають подібні виклики. Тому цю статтю не варто приймати близько до серця тому, хто впізнав себе в моєму тексті.
useEffect
Для того, щоб повністю зрозуміти, що саме робить useEffect, в першу чергу необхідно повністю розуміти життєвий цикл компонента, інакше ви завалите будь-яку співбесіду.
У 2025 році на курсах з фронтенду дуже поверхнево проходять класові компоненти, а якщо людина вчиться самостійно, то сучасні roadmaps можуть взагалі не містити цю тему. Тому деякі новачки можуть просто не знати про те, що до появи хуків усі «серйозні» речі — стан і життєвий цикл — існували тільки в класових компонентах. В той час як функціональні (з 2015) працювали як звичайні функції — рендери. Я це до того, що на методах життєвого циклу класового компонента було дуже легко зрозуміти сам життєвий цикл.
Так ось, при переході на функціональні компоненти на заміну деяких методів життєвого циклу прийшов хук useEffect. Одразу хочу зробити ремарку, що хуки не працюють в серверних (SSR) компонентах, тому мова в цій статті виключно про чистий React та клієнтські компоненти.
А тепер давайте детально зіставимо методи класу з використанням useEffect та інших «плюшок» функціональних компонентів та одночасно розберемося з життєвим циклом.
1) componentDidMount
Цей метод викликався один раз при монтуванні (народженні) компонента. Фактично сам по собі useEffect, не залежно від умов, завжди працює як componentDidMount. Неважливо, чи є в ньому масив залежностей, чи порожній він. При монтуванні компонента useEffect виконається завжди.
// componentDidMount
useEffect(() => {
console.log('Hello');
});
// componentDidMount
useEffect(() => {
console.log('Hello');
}, []);
// componentDidMount
useEffect(() => {
console.log('Hello');
}, [dependency]);
Ми зʼясували, що першим кроком життєвого циклу компонента є Монтування.
2) shouldComponentUpdate
Сама назва цього методу, як в принципі й будь-якого методу, пояснює його суть. Чи повинен компонент оновитися?
Як ми знаємо, зміна локального стану або пропсів завжди викликає ререндер. Так ось, shouldComponentUpdate — це унікальний метод, який дозволяв «відсікти» зайві перерендери, повернувши false, і тим самим зберегти ресурси. У світі функціональних компонентів ми більше не пишемо цього методу вручну, але його ідея нікуди не зникла:
Перевірку пропсів тепер виконує React.memo. Коли ми огортаємо компонент у memo, React автоматично порівнює попередні та нові пропси. Також ми можемо додати колбек areEqual вручну, який передається в memo другим аргументом, приймає в себе попередні та нові пропси. В ній ми можемо прописати будь-які потрібні нам умови, і поки вона повертає true, ререндер пропускається* так само як і при shouldComponentUpdate(false).
// UserCard ререндериться тільки коли ID користувача було
// змінено, навіть якщо всередині об’єкта створюються нові
// поля (тобто коли areEqual === false)
import { memo } from 'react';
function UserCard({ user }) {
console.log('render:', user.name);
return <div>{user.name}</div>;
}
export default memo(
UserCard,
(prevProps, nextProps) => prevProps.user.id === nextProps.user.id
);
* Якщо під час ререндеру батьківського компонента створюються нові функції або об’єкти, які ми передаємо в props дочірнього, вони матимуть нові посилання й можуть спричинити зайвий ререндер. У такій ситуації слід кешувати функції через useCallback, а об’єкти, масиви й обчислені значення — через useMemo. Поки залежності цих хуків не змінюються, React повертає ті самі посилання, бачить, що нічого не змінилося, і пропускає оновлення.
Що стосується ререндеру при зміні локального стейту, то йому теж можна запобігти, використовуючи useRef** замість useState.
// Викличе ререндер:
const [state, setState] = useState("");
const handleClick = () => {
setState("Hello world");
};
// Не викличе ререндер:
const state = useRef("");
const handleClick = () => {
state.current = "Hello world";
};
*Оскільки useRef не викликає ререндер, то варто бути уважним, якщо ми помістили туди якісь дані, які повинні відображатися в DOM. Бо ми не побачимо ніяких оновлень контенту, який зберігається в useRef на сторінці. Використовувати його варто лише за потреби в правильному контексті.
Окрім того, що ми швидко і, сподіваюся, легко розібралися з оптимізацією і кешуванням даних, ми зʼясували, що другим кроком життєвого циклу компонента є Оновлення (Ререндер).
3) componentDidUpdate
Цей метод також вказує на те, що в життєвому циклі є оновлення. Як зрозуміло з назви, він спрацьовує після ререндеру компонента. В якості його заміни ми можемо використовувати useEffect у двох варінтах:
1. Без масиву залежностей.
2. З заповненим масивом залежностей.
useEffect без масиву залежностей буде виконувати переданий в нього колбек при кожному ререндері, а ось з заповненим масивом, цей колбек буде виконуватися тільки в разі оновлення значення якоїсь змінної, яку ми помістили в масив.
// Завжди виконується
const useEffect(() => {
console.log("Rerendered");
});
componentDidUpdate() {
console.log("Rerendered");
}
// Тільки якщо змінилися змінні, які лежать в масиві залежностей
const useEffect(() => {
console.log("Rerendered");
}, [someDependency]);
Як схожого ефекту добивалися в componentDidUpdate*:
componentDidUpdate(prevProps, prevState) {
if (prevState.count !== this.state.count) {
console.log("Do something");
}
}
*Зверніть увагу, що він викликався в будь-якому разі після ререндеру, а умови, за якими ми визначали, чи будемо виконувати якісь дії, ми прописували вручну. Але хочу нагадати ще раз, що ці приклади з useEffect також завжди відпрацюють в якості метода componentDidMount, тому в деяких випадках в ньому також треба прописувати деякі умови вручну.
4) componentWillUnmount
Це не останній з усіх існуючих методів, але він «прощальний» для нашого компонента. Виконується він безпосередньо перед розмонтуванням компонента, тобто повного видалення його з DOM-дерева. В більшості випадків він використовувався для скидання слухачів подій та очищення таймерів, але міг виконувати й деякі інші завдання, наприклад, запис якоїсь інформації в localStorage і навіть запити на сервер.
Аналог цього метода в useEffect — це колбек, який ми можемо повернути з колбеку, переданого в нього. В обидвох випадках цей функціонал іменується як cleanup-фаза. Мається на увазі, що в цей момент ми повинні прибрати все сміття з памʼяті після компонента. І хоч робити якісь додаткові сайд-ефекти в цей момент не заборонено, це вважається поганою практикою і може викликати непередбачену поведінку, особливо в useEffect.
useEffect(() => {
window.addEventListener("resize", handleResize);
// Функція cleanup виконується при розмонтуванні компонента
// Аналог componentWillUnmount
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
Було б неправильно не сказати, що для того, щоб функція cleanup виконувалася тільки при розмонтуванні компонента, ми обовʼязково повинні вказати порожній масив залежностей. Інакше вона може бути викликана перед кожним ререндером чи зміною залежності. Детальну інформацію вже самі знайдете, бо стаття і так вийшла немаленькою.
Double-Invoke у StrictMode
У dev-режимі React 18+ двічі викликає useEffect (і його cleanup відповідно) одразу після монтування. Це навмисна «стрес-тест»-поведінка, аби виявити небезпечні сайд-ефекти, на продакшені цього не буде.
Підсумуємо
useEffect — універсальний хук, який у правильній комбінації з React.memo, useMemo та useCallback повністю перекриває більшість* класових методів життєвого циклу.
*Так, існують інші методи в класових компонентах, які належать до життєвого циклу, але ви цю статтю і до завтра не закінчили б читати.
Повертаючись до співбесіди та до кандидата
Взагалі я розумію, чому зараз всі junior-розробники з більш-менш якимось досвідом хочуть позбутися свого статусу і називатися вже «мідлами». Одна з вагомих причин це те, що, «junior-розробниками» себе зараз називають ті, хто ще навіть не trainee.
Людина ще навчається на курсах або тільки закінчила їх, ще не має навіть жодного пет-проєкту (або навіть і має), а вже позиціонує себе як «Junior frontend engineer». Тому справжнім джунам стає «не солідно» досі «ходити в джунах», особливо маючи більше року досвіду. Тому що між «джуном» одразу після курсів і джуном з роком комерційного досвіду, на мою думку, просто прірва у знаннях та навичках.
Це сучасні реалії, так робить переважна більшість початківців і навіть я робив так само. Але на це звертаєш увагу і можеш дати якусь оцінку тільки вже після певного досвіду.
Ще одна вагома причина, як на мене, — банальна сліпа впевненість у собі, підживлена бажанням заробляти більше. Саме вона й штовхає людину голосно кричати: «Та я вже все знаю!». А досвідчені розробники чудово знають просту річ: щойно чуєш таке — перед тобою, швидше за все, джун.
Сподіваюся, моя стаття була корисною та доступною для розуміння. Можливо, комусь вона допомогла зняти рожеві окуляри, але, сподіваюся, що нікого не образила і не демотивувала. Ну і не забувайте, що скільки людей — стільки й думок, тому не сприймайте все близько до серця та не беріть на себе зобовʼязання перед всесвітом щось комусь довести або спростувати мої субʼєктивні думки, якщо вони не збігаються з вашими.
Але з радістю чекаю ваші думки в коментарях :)
28 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарівДякую автору за статтю, але мені було б цікаво подивитися то відео, мені здається я знайшов хто саме проводив співбесіду але з ким? Хоч якусь підказку плз
Можу припустити що це ця співбесіда
www.youtube.com/live/YpKUkpd3CRA
Дякую
Якщо не помиляюсь, did update не виконувався на перший рендер, і було складно переходи з класссів, коли треба був сам did update
можна додати цей випадок, часто використовується, це саме старий did update
Шкода до речі що з теорії по Реакту викинули хоч поверхневе проходження по класовим компонентам, бо думаю багато є легасі де вони ще використовуються та і взагалі не зовсім з хуків зрозумієш весь життєвий цикл компонента.
А мені стаття дуже сподобалась, з задоволенням прочитаю будь-які ваші інші
Дякую за коментар!
Дякую за статтю!
Автору, дякую за статтю.
Всім іншим порадникам, є бажання щось змінювати пишіть статті, залишайте посилання на цю статтю, тоді висловлюйте думки/критику/захоплення з прикладами «на 4 рік під капотом» і т.д.
PS: Розумні статтями «міряються», мурді читають і втихую висновки роблять.
Всім миру.
Дякую за коментар і за Вашу позицію)
Почитав коментарі і співчуваю автору. Концентрація токсиків і духоти якась завищена
Дякую, за коментар і підтримку) Зате це показує, що люді не байдужі, що люди читають статті і що спільнота фронтендерів існує, вона жива й активна, а це найголовніше)
На четвертому році захоплення можна вже і про хуки «під капотом» писати.
Звісно можна. Як і про багато інших речей. В статті написано про те, що мене надихнуло написати її саме на цю тему.
)))
Теория такая штука... Вопрос — автор готов показать проэкт(хотя бы и пэт) с идеальным кодом?) Ну, шоб прям была уверенность, что ни одного комментария/замечания/предложения по коду не будет?
Немає межі досконалості, в будь якому коді завжди є що вдосконалити та переосмислити)
Отож ;)
А при чому тут одне до іншого, ну так код можна написати без зауважень. Якщо звичайно їх робить адекватна людина. В нас на проекті логіка проста, якщо не знаєш як зробити краще, то просто не критикуєш.
До чого теорія до практики в розрізі цього обговорення? На таке навіть відповідати нема сенсу. Щастя, простої логіки і успіхів, бро))
Згоден щодо того, що критика має бути коструктивною.. ну, бажано. Але якшо це тотальний підхід — то срака проєкту
А в Статті порівняння функціоналу який юзався 6 років тому =) Чекаю статтью про jQuery та ES6
Хочете сказати, що зараз вже не актуально знати та використовувати вищеперераховані хуки?
я думаю ви здивуєтесь скільки сайтів ще використовують jQuery
потрібно написати статтю порівняння jQuery vs React
Візьметесь за цю справу?)
І ше статтю про тепле vs м’яке з урахуванням IRL і вимог бізнесу))
Це все розповідають на курсах, давно.
Колись дивився безкоштовний курс Віталія Рубана, там про це теж було.
Прорекламую цей курс, для тих хто не знає
t.me/reactbeginners/316