Récemment, je travaillais sur React Hooks et je me suis retrouvé coincé avec un problème/un doute ?

Vous trouverez ci-dessous une implémentation de base pour reproduire le problème. Ici, je bascule simplement sur la variable flag (un état) en cliquant sur le bouton.

  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>
  );

J'ai trouvé un autre moyen de surmonter ce problème, comme utiliser useRef ou useReducer, mais est-ce correct ou existe-t-il un autre moyen de résoudre ce problème avec useState uniquement?

De plus, ce serait vraiment utile si quelqu'un explique pourquoi nous obtenons l'ancienne valeur de l'état dans le setTimeout.

URL du bac à sable - https://codesandbox.io/s/xp540ynomo

27
manish keer 16 mars 2019 à 18:37

2 réponses

Meilleure réponse

Cela se résume à la façon dont les fermetures fonctionnent en JavaScript. La fonction donnée à setTimeout obtiendra la variable flag du rendu initial, puisque flag n'est pas muté.

Vous pouvez plutôt donner une fonction en argument à toggleFlag. Cette fonction obtiendra la valeur flag correcte comme argument, et ce qui est renvoyé de cette fonction est ce qui remplacera l'état.

Exemple

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>
16
Tholle 16 mars 2019 à 15:42

La fonction donnée à setTimeout obtiendra la variable flag de la fonction _onClick. La fonction _onClick est créée à chaque rendu et "stocke" la valeur que la variable flag obtient sur ce rendu.

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>
  );
}

Console:

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
0
UjinT34 16 mars 2019 à 16:32