26

In the code snippet below, when I click on Change button to change the value of isLoading , nothing happened (isLoading is false).

const App = (props) => {
  const [isLoading, setIsLoading] = useState(false)

  const buttonHandler = () => {
    setIsLoading(current => !current)
    console.log(isLoading) // is false 
  }

  return (
    <div>
      <button onClick={buttonHandler} type="button">
        Change
      </button>
    </div>
  )
}

I tried to change isLoading with the following ways but does not affect:

1-setIsLoading(current => !current)
2-setIsLoading(!isLoading)
3-setIsLoading(true)
  • 1
    State changes are asynchronous so you can't `console.log` them on the next line and see them changed. Try printg `isLoading` directly below your `useState` line, that way it will print when the componetn re-renders with the new state – Jayce444 Apr 06 '20 at 06:46
  • This question is asked nearly daily. Please search SO before asking. Does this answer your question? [React setState not updating state](https://stackoverflow.com/questions/41446560/react-setstate-not-updating-state) – Drew Reese Apr 06 '20 at 06:46
  • 2
    State changes are asynchronous. So use component tab in developer tools to monitor the state changes. – shalitha senanayaka Apr 06 '20 at 06:50

6 Answers6

22

setIsLoading is an async function and you cannot get the state value immediately after update.

setState actions are asynchronous and are batched for performance gains. setState() does not immediately mutate this. Thus the setState calls are asynchronous as well as batched for better UI experience and performance. This applies on both functional/Class components.

From React documentation

React may batch multiple setState() calls into a single update for performance. Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state. You could read more about this here

If you want to get the updated state value then use useEffect hook with dependency array. React will execute this hook after each state update.

const {useEffect, useState } = React;

const App = (props) => {
  const [isLoading, setIsLoading] = useState(false)
  const buttonHandler = () => {
    setIsLoading(current => !current)
  }

  useEffect( () => {
    console.log(isLoading);
}, [isLoading]);

  return (
    <div>
      <button onClick={buttonHandler} type="button">
        Change
      </button>

      {isLoading? "Loading...": null}
    </div>
  )
}

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

    <div id="root">
      loading.....
    </div>
Sohail Ashraf
  • 10,078
  • 2
  • 26
  • 42
3

This is the expected behavior. You may want to use useEffect to access the latest value.

Here is a thread discussing the same issue: useState set method not reflecting change immediately

Hope this helps!

Yash Joshi
  • 2,586
  • 1
  • 9
  • 18
1

State and hooks are asynchronous. You won't see a change in isLoading directly after calling set..., but only on the next render of the component, which will happen "soon enough".

If you do print the value of the statelet (as a string; false renders as nothing), you can see the change:

return (
    <div>
      <button onClick={buttonHandler} type="button">
        Change (now {"" + isLoading})
      </button>
    </div>
  )
AKX
  • 152,115
  • 15
  • 115
  • 172
0
const [isLoading, setIsLoading] = useState(false)

Don't use useState like this, you cannot get the state value immediately after update.

Reference: https://reactjs.org/docs/state-and-lifecycle.html#state-updates-may-be-asynchronous

So, instead of this use: useRef (If you want to keep the object for the full lifetime of the component).

i.e.

const isLoading = useRef(false);

And later you can assign it as:

isLoading.current = true;

And you can use it as:

console.log(isLoading.current);

Reference: https://reactjs.org/docs/hooks-reference.html

Indranil
  • 2,229
  • 2
  • 27
  • 40
0
<button onClick={buttonHandler} type="button">
       {isLoading ? "True" ; "False"}
      </button>

We use a Ternary operator

condition ? exprIfTrue : exprIfFalse

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator

lb2020
  • 113
  • 1
  • 7
0

in useSate hook the setSate is asynchronous. You can use the console.log() outer of buttonHandler. You will see the value will be updated. As asynchronous nature, you are not seeing the updated result.

Akash
  • 21
  • 1
  • 4