MiiZZo
@MiiZZo
JavaScript, React, NodeJS, Student

Почему при изменении состояния react-компонента в setInterval, значение состояния каждый раз сбрасывается к исходному?

Код подобный этому
import React, { useState, useEffect } from 'react';

export const App = (): JSX.Element => {
    const [count, setCount] = useState(0);
    
    useEffect(() => {
        setInterval(() => {
            console.log(count);
            setCount(count + 1);
        }, 100);
    }, []);

    return (
        <div>
            { count }
        </div>
    )
}

В консоли вижу изначальное значение count, т.е - 0, что я делаю не так?
  • Вопрос задан
  • 1230 просмотров
Решения вопроса 3
MiiZZo
@MiiZZo Автор вопроса
JavaScript, React, NodeJS, Student
Пояснение из документации React:
https://ru.reactjs.org/docs/hooks-faq.html#what-ca...
function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(count + 1); // Этот эффект зависит от переменной состояния `count`
    }, 1000);
    return () => clearInterval(id);
  }, []); //  Баг: `count` не указан в качестве зависимости

  return <h1>{count}</h1>;
}

Пустой набор зависимостей [] означает, что эффект будет выполняться только один раз, когда компонент монтируется, а не при каждом повторном рендере. Проблема в том, что внутри обратного вызова setInterval значение count не изменяется, потому что мы создали замыкание со значением count, установленным в 0, как это было при выполнении обратного вызова эффекта. Каждую секунду этот обратный вызов вызывает setCount(0 + 1), поэтому счетчик никогда не превышает 1.

Если переписать список зависимостей как [count], то баг будет устранён, но это приведёт к сбрасыванию интервала при каждом изменении. Такое поведение может быть нежелательно. Чтобы исправить это, мы можем применить форму функционального обновления хука setState, которая позволяет указать, как должно меняться состояние, не ссылаясь явно на текущее состояние:

function Counter() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const id = setInterval(() => {
      setCount(c => c + 1); // ✅ Эта строчка не зависит от внешней переменной `count`
    }, 1000);
    return () => clearInterval(id);
  }, []); // ✅ Наш эффект не использует никаких переменных из области видимости компонента

  return <h1>{count}</h1>;
}
Ответ написан
Комментировать
0xD34F
@0xD34F Куратор тега React
Потому что count, к которому вы добавляете единицу - это тот, который был при первом рендеринге компонента. Нужен доступ к актуальному - вместо числа передавайте в setCount функцию: setCount(count => count + 1);.

что я делаю не так?

Одно из двух: не читаете документацию, или взялись за react не зная языка.
Ответ написан
hzzzzl
@hzzzzl
замыкания и вся связанная с этим фигня, в функции будет значение count на момент создания функции

к счастью, можно сделать так

useEffect(() => {
        setInterval(() => {
            console.log(count);  //  всегда будет 0
            setCount(prev => prev + 1);   // prev будет актуальным
        }, 100);
    }, []);
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы