Готові перевірити себе? 👇
1. Чому useEffect може працювати з «неактуальним» state?
📌 Питання:
Що буде виводити
console.log, якщо натискати кнопку?
function Counter() {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
console.log(count);
}, 1000);
return () => clearInterval(id);
}, []);
return (
<button onClick={() => setCount(c => c + 1)}>
{count}
</button>
);
}
📌 Очікувана відповідь:
console.log продовжить виводити 0.Причина — stale closure.
Callback усередині
setInterval замикає значення count із того render, у якому був створений Effect. У нашому випадку це 0.При цьому
count — reactive value, яка використовується всередині Effect, але відсутня в dependency array. Тому Effect не синхронізується з наступними значеннями count.До речі, правило
exhaustive-deps у eslint-plugin-react-hooks саме на це й поскаржиться.Якщо Effect має реагувати на зміну
count, його можна додати в dependencies:
useEffect(() => {
const id = setInterval(() => {
console.log(count);
}, 1000);
return () => clearInterval(id);
}, [count]);
Тоді після зміни
count React спочатку виконає cleanup попереднього Effect, а потім запустить Effect знову з актуальним значенням.Важливий нюанс: у цьому варіанті interval буде перестворюватися при кожній зміні
count, тому в реальному коді можуть бути доречними й інші рішення залежно від задачі.📌 Що перевіряє інтерв'юер:
Чи розуміє кандидат JavaScript closures, React render model, Effects та dependency array, а не просто знає синтаксис
useEffect.