React - useState - почему функция setTimeout не имеет последнего значения состояния? - PullRequest
2 голосов
/ 16 марта 2019

Недавно я работал над React Hooks и застрял с одной проблемой / сомнением?

Ниже приведена базовая реализация для воспроизведения проблемы. Здесь я просто переключаю flag (состояние) переменную нащелчок по кнопке.

  const [flag, toggleFlag] = useState(false);
  const data = useRef(null);
  data.current = flag;

  const _onClick = () => {
    toggleFlag(!flag);
    // toggleFlag(!data.current); // working

    setTimeout(() => {
      toggleFlag(!flag); // does not have latest value, why ?
      // toggleFlag(!data.current); // working
    }, 2000);
  };

  return (
    <div className="App">
      <button onClick={_onClick}>{flag ? "true" : "false"}</button>
    </div>
  );

Я нашел другой способ преодоления этой проблемы, такой как использование useRef или useReducer, но это правильно или есть какой-то другой способ решить эту проблему только с помощью useState?

Также, было бы очень полезно, если бы кто-нибудь объяснил, почему мы получаем старое значение состояния внутри setTimeout .

URL песочницы - https://codesandbox.io/s/xp540ynomo

Ответы [ 2 ]

2 голосов
/ 16 марта 2019

Это сводится к тому, как замыкания работают в JavaScript. Функция, присвоенная setTimeout, получит переменную flag из начального рендера, поскольку flag не является мутированным.

Вместо этого вы можете задать функцию в качестве аргумента toggleFlag. Эта функция получит правильное значение flag в качестве аргумента, и то, что возвращается из этой функции, это то, что заменит состояние.

Пример

const { useState } = React;

function App() {
  const [flag, toggleFlag] = useState(false);

  const _onClick = () => {
    toggleFlag(!flag);

    setTimeout(() => {
      toggleFlag(flag => !flag)
    }, 2000);
  };

  return (
    <div className="App">
      <button onClick={_onClick}>{flag ? "true" : "false"}</button>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="root"></div>
0 голосов
/ 16 марта 2019

Функция, заданная для setTimeout, получит переменную flag из функции _onClick. Функция _onClick создается при каждом рендеринге и «сохраняет» значение, которое переменная flag получает в этом рендере.

function App() {
  const [flag, toggleFlag] = useState(false);
  console.log("App thinks that flag is", flag);

  const _onClick = () => {
    console.log("_onClick thinks that flag is", flag);
    toggleFlag(!flag);

    setTimeout(() => {
      console.log("setTimeout thinks that flag is", flag);
    }, 100);
  };

  return (
    <div className="App">
      <button onClick={_onClick}>{flag ? "true" : "false"}</button>
    </div>
  );
}

Консоль

App thinks that flag is false

_onClick thinks that flag is false
App thinks that flag is true
setTimeout thinks that flag is false

_onClick thinks that flag is true
App thinks that flag is false
setTimeout thinks that flag is true
...