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>

Answer from Sohail Ashraf on Stack Overflow
Top answer
1 of 6
25

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>

2 of 6
4

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!

🌐
Reddit
reddit.com › r/reactjs › usestate(boolean) vs usestate(false)
r/reactjs on Reddit: useState(Boolean) vs useState(false)
February 18, 2021 -

Is it a thing to set default state values using primitive type constructors when applicable? e.g.

// Boolean (defaults to false)
const [active, setActive] = useState(Boolean);
const [active, setActive] = useState(false);

// String (defaults to "") 
const [searchTerm, setSearchTerm] = useState(String); 
const [searchTerm, setSearchTerm] = useState("");

// Number (defaults to 0)
...

Another developer who was learning React at the same time as me said I should do it this way and I pretty much have been doing it this way for the last 2 years.

I figured it helped me write more declarative code and never gave it much more thought until this week. I've been trying to find any information discussing this but haven't found a thing.

Is there any explanation for or against this that people can think of? Do many other people declare state like this?

Discussions

Better way to use useState hook for setting the boolean state in React
I've just started learning React and got to know about the useState hook. I came across two different ways for setting the state for boolean data. So are these two approaches identical and if not, ... More on stackoverflow.com
🌐 stackoverflow.com
Bug: Boolean Values is not updating while submitting the form for the first time
React version: 17.0.2 · 1.In above code when you click on submit button for the first time , the emailErr and passwordErr values are not updating even the code is logically correct More on github.com
🌐 github.com
7
March 9, 2022
reactjs - Cannot set useState hook value to opposite boolean - Stack Overflow
I'm creating a DatePicker and using useState hook to manage it's visibility. On div click I've added the event listener which changes value, but it didn't work as I expected. It works only the firs... More on stackoverflow.com
🌐 stackoverflow.com
July 27, 2019
reactjs - React JS Typescript usestate with boolean - Stack Overflow
function App() { const [ isLogged, setIsLogged ] = React.useState(false); const changedLogging = () => { setIsLogged( !isLogged ); } return ( { !isLogged? More on stackoverflow.com
🌐 stackoverflow.com
🌐
usehooks-ts
usehooks-ts.com › react-hook › use-boolean
useBoolean | usehooks-ts
Will throw an error if defaultValue is an invalid boolean value. ... The useBoolean return type. import { useCallback, useState } from 'react' import type { Dispatch, SetStateAction } from 'react' type UseBooleanReturn = { value: boolean setValue: ...
🌐
CodeSandbox
codesandbox.io › s › usestate-boolean-basic-example-iepcl
useState boolean basic example - CodeSandbox
January 30, 2020 - useState boolean basic example by sandagolcea using react, react-dom, react-scripts
Published   Jun 10, 2019
Author   sandagolcea
Top answer
1 of 6
12

Since, as often in code, a simple example paints a thousand words, here's a simple CodeSandbox demo to illustrate the difference, and why, if you want an update based on the value of the state at the point of update, the "updater function" (Approach 1) is best:

https://codesandbox.io/s/stack-overflow-demo-nmjiy?file=/src/App.js

And here's the code in a self-contained snippet:

<div id="root"></div><script src="https://unpkg.com/[email protected]/umd/react.development.js"></script><script src="https://unpkg.com/[email protected]/umd/react-dom.development.js"></script><script src="https://unpkg.com/@babel/[email protected]/babel.min.js"></script>
<script type="text/babel" data-type="module" data-presets="env,react">

function App() {
  const [count, setCount] = React.useState(0);

  // this uses the "good way" but it doesn't really matter here
  const incrementPlain = () => setCount((oldCount) => oldCount + 1);

  const incrementWithTimeoutBad = () =>
    setTimeout(() => setCount(count + 1), 3000);
  const incrementWithTimeoutGood = () =>
    setTimeout(() => setCount((oldCount) => oldCount + 1), 3000);

  return (
    <div>
      <div>Current count: {count}</div>
      <div>
        <button onClick={incrementPlain}>
          Increment (doesn't matter which way)
        </button>
      </div>
      <div>
        <button onClick={incrementWithTimeoutBad}>
          Increment with delay (bugged)
        </button>
        <button onClick={incrementWithTimeoutGood}>
          Increment with delay (good)
        </button>
      </div>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));

</script>

Here we have a simple numeric "count" state which is displayed in the markup, together with 3 different buttons that all increment it.

The one on top just does a direct increment - I happen to have used the function form ("approach 1") here because I prefer this style for reasons that will hopefully become clear, but as my comment says, it doesn't actually matter here.

The two below use the two different approaches you outline in the question, and do so after a delay. I've done this with setTimeout here just to be simple - while this isn't particularly realistic, similar effects are commonly seen in real apps where actions call an API endpoint (and even though one hopes that doesn't normally take as long as 3 seconds, the same problems can always be observed with quicker requests - I've just slowed it down to be easier to trigger the bug).

To see the difference, try the following with each of the 2 bottom buttons:

  • click the button
  • click the button on top (to increment the count again) BEFORE the 3-second timeout is up

You should see a clear difference in behaviour:

  • with "approach 1" (button on the right, which I'm calling "good" here), the count increments a second time after the timeout is finished.
  • with "approach 2" (button on the left, which I've called "bugged"), there is no further increment from the value produced by the intermediate click on the top button, no matter how long you wait

(You can see this more dramatically if you click the bottom button multiple times quickly, then the top one once. And for an even more counterintuitive effect, try pressing the "bugged" bottom button one or more times, then clicking the top button more than once, all within the 3 second time interval.)

Why does this happen? Well, the "buggy" behaviour happens because the function inside the setTimeout is a closure over the outer variable count which is in the scope of the full component function. That means that when it's called with count + 1 as the argument, it will update the count to 1 more than whatever it was at the point the function was defined. Say you do the above sequence from first loading the component where count is 0, then the more detailed sequence of what happens is:

  • the bottom button click schedules a callback to happen in 3 seconds' time. Since count at that point is equal to 0, its argument, count + 1 is equal to 1.
  • the top button click rerenders the component, with count now equal to 1.
  • the callback set up at the first step later triggers, and sets the count to 1. This doesn't cause any noticeable chance, because the count was already 1. (If you tried clicking the top button multiple times, so it now shows 2 or more, this will actually decrement the counter, because as I'm explaining, it will always get set to 1.)

If you know a little bit about JS closures, you might wonder why the count that is accessed in the closure is still 0. Wasn't it previously updated to 1? No, it wasn't, and that's the bit that might be counterintuitive. Notice how count is declared with const? That's right - it never actually changes. The reason the UI updates is because setCount causes React to rerender your component, which means the whole outer function corresponding to the component is called again. This sets up a whole new environment, with a new count variable. React's internals ensure that the useState call now gives back 1 for the current count, which is therefore the value in that new "instance" of the component - but that's irrelevant from the point of view of the function that was put in the event queue to fire after 3 seconds. As far as it's concerned, the count variable - no longer in scope but "remembered" inside that callback as all closed-over variables are - has never changed from 0. The count that's equal to 1 is in a different scope entirely, and forever inaccessible to that first callback.

How does the function argument form - "approach 1" - get round this? Very easily. It doesn't hold any closure at all - the variable inside that function, which I've called oldCount here for the sake of both accuracy and to disambiguate from the outer count - has nothing to do with the count outside. It's the argument to a function that React itself will call internally. When React does call the function, it always supplies the "most up-to-date" state value it has. So you don't have to worry about "stale closures" or anything like that - you're saying "whatever the most recent value was, update the count to be one more than that", and React will take care of the rest.

I've called approach 2 "bugged" here because I think it's reasonable to expect an increment to happen after the timeout, if you've clicked a button that you've set up to do an increment. But this isn't always what you want. If you genuinely wanted the update to be based on the value at the point the button was first clicked, then of course you will prefer Approach 2, and Approach 1 will seem bugged. And in a sense that's more often the case. I highly recommend reading this post by Dan Abramov - one of the core React developers - that explains a crucial difference between class components and functions, that's based on many of the same arguments about closures that came into play here, where normally you do want the event handlers to reference values as they were at the time of render, not when they actually fire after an API request or timeout.

But that post doesn't have anything to do with the "approach 1" form of state-updating functions, which isn't even mentioned in the article. That's because it's irrelevant to the examples given - there'd be no (sensible) way to rewrite those examples to use it. But when you do want to update a state value based on its previous value - as could happen with negating a boolean value, as in the OP example, or incrementing a counter as in mine, I would argue that it's more natural that you always want that "previous value" to be up to date. There are 2 buttons which both should increment a value, albeit in different ways - I think it's reasonable to call it bugged if clicking both of them, depending on the timing, may only increment once in total.

But that's of course up to each individual component or application to decide. What I hope I've done here is explain what the difference is, and give you a basis to choose which might be best. But I do believe that 90+% of the time, if you have the option of using the function argument ("approach 1"), it will be better, unless you know it isn't.

2 of 6
6

the first approach setIsChanged((prevState) => !prevState)

to make sure that you always have the last state before changing it.

🌐
DEV Community
dev.to › alexkhismatulin › update-boolean-state-right-with-react-hooks-3k2i
Update boolean state right with React Hooks - DEV Community
May 11, 2020 - I will create two separate state handlers: one for our boolean state and another one for storing a random number. const BasicBooleanState = () => { const [isToggled, setIsToggled] = React.useState(false); const toggle = React.useCallback(() => setIsToggled(!isToggled)); const [randomNumber, setRandomNumber] = React.useState(Math.random()); const generateRandomNumber = React.useCallback( () => setRandomNumber(Math.random()), [], ); return ( <div> <div> Current random number is <b>{randomNumber}</b> <button style={{ marginLeft: '10px' }} onClick={generateRandomNumber}> regenerate </button> </div> <div> Boolean is set to <b>{String(isToggled)}</b>. </div> <RendersCounter onClick={toggle} /> </div> ); }
🌐
YouTube
youtube.com › techinfo yt
useState Boolean Value Issue | React Hooks Tutorials - YouTube
video contains: solution of Boolean value with useState HookWelcome to React JS Hooks Series in this video you will learn about complete react js hooks with...
Published   March 11, 2022
Views   2K
Find elsewhere
🌐
React
react.dev › reference › react › useState
useState – React
It only affects what useState will return starting from the next render. 1. Counter (number) 2. Text field (string) 3. Checkbox (boolean) 4.
🌐
CodingDeft
codingdeft.com › posts › how-to-use-react-usestate-with-typescript
How to add type to useState while using TypeScript with React | CodingDeft.com
July 30, 2022 - If you need to declare a state to store a boolean value, then you can declare it as shown below: 1const [autoPlay, setAutoPlay] = useState<boolean>(false)
🌐
GitHub
github.com › facebook › react › issues › 24063
Bug: Boolean Values is not updating while submitting the form for the first time · Issue #24063 · facebook/react
March 9, 2022 - import React,{useState} from 'react'; import './App.css'; function App() { const [email,setEmail]=useState(""); const [password,setPassword]=useState(""); const [emailErr,setEmailErr]=useState(false); const [passwordErr,setPasswordErr]=useState(false); const loginSubmit=(e)=>{ e.preventDefault(); if(email.length<5){ setEmailErr(true); }else{ setEmailErr(false); } console.log('this is email: ' + email); console.log('this is emailErr: ' + emailErr); if(password.length<5){ setPasswordErr(true) }else{ setPasswordErr(false); } console.log('this is password: ' + password); console.log('this is passw
Author   chamakurich
🌐
Bobby Hadz
bobbyhadz.com › blog › react-toggle-boolean-state
How to toggle a Boolean state in React | bobbyhadz
Toggle the boolean based on the current value. ... Copied!import {useState} from 'react'; export default function App() { // 👇️ Initialize boolean to false const [isLoading, setIsLoading] = useState(false); const toggleIsLoading = () => { // 👇️ Passed function to setState setIsLoading(current => !current); }; return ( <div> <button onClick={toggleIsLoading}>Toggle loading state</button> {isLoading && <h2>bobbyhadz.com...</h2>} </div> ); }
🌐
Selftaughttxg
selftaughttxg.com › 2023 › 04-23 › creating-a-true-false-toggle-in-react-with-usestate-hook-for-beginners
Creating a True/False Toggle in React with useState Hook for Beginners |
April 14, 2023 - This code below creates a variable called count that represents the current state value and a function called setCount that is used to update that value. The useState function is used to add state to a functional component, and the 0 inside the parentheses is the initial value of count.
🌐
GitHub
gist.github.com › brookback › 26622ff6a7d11ed34f6e321cf79e81a4
A React hook for getting a single boolean value for when pairs in an array change. · GitHub
A React hook for getting a single boolean value for when pairs in an array change. ... import React, { useState } from 'react'; interface Props { text: string; } const MyComponent = (props: Props) => { const [currentText, setText] = ...
🌐
Dommagnifi
dommagnifi.co › 2020-12-03-toggle-state-with-react-hooks
Toggle State With React Hooks - Dominic Magnifico
Now, whatever the state of our toggle state variable is, clicking the button will set it to the opposite boolean value. A true toggle! With our state being properly toggled we can do some fun things in React. One of the most common things I use a toggle for is hiding or showign markup on the page. For example, a dropdown menu. We don't necessarily need the DOM markup for a hidden dropdown menu on the page on render. But we definitely want that markup when we click the button. This can be achieved like so: import React, { useState } from 'react' const MyComponent = () => { const [toggle, setToggle] = useState(false) return( <> <button onClick={() => setToggle(!toggle)}>Toggle Dropdown Markup</button> {toggle && ( <ul> <li>Show me</li> <li>Only when</li> <li>Toggle === true</li> </ul> )} </> ) }
🌐
Jstopics
jstopics.com › reactjs › usestate-with-typescript
UseState with TypeScript | JSTopics
But what is *state* in React components? Let us have a look. We can think of a state as a small piece of memory that holds our component's data. It can be a variable called showModal. This variable holds data (boolean) that tell our component if it should show a modal.
🌐
Medium
medium.com › @IkeoluwaAshade › clicking-button-to-toggle-a-boolean-state-8257b70fb293
Clicking button to toggle a Boolean state | by Ikeoluwa Ashade | Medium
June 20, 2024 - toggle is the current state variable, which is a boolean (i.e true or fasle). !toggle is the negation of toggle. If toggle is true, !toggle will be false and vice versa ... setToggle(!toggle) updates the state to the opposite of its current value.
🌐
Lukaspolak
lukaspolak.com › blog › toggle-boolean-state-effectively-in-react
Toggle Boolean State Effectively in React | Lukas Polak
The aim of this article was not to show shorter code or to create some library but to show how to use the useReducer hook instead of the useState hook for managing the state that should be toggleable.