React setState hook not working with useEffect

All we need is an easy explanation of the problem, so here it is.

I have the following code. The idea is to create a simple loading for a mock component

const [loading, setLoading] = useState(false)

  function mockLoading () {
    setLoading(true)
    console.log('1', loading)
    setTimeout(() => {
      setLoading(false)
      console.log('2', loading)
    }, 3000)
  }

  useEffect(() => {
    mockLoading()
  }, []);

But for some reason, setLoading is not working.
Console.log 1 and 2 both are false

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

A couple of things. One issue is that when you set the state, the state does not immediately update. So your first console.log will see the old state. Not only that, but when your effect is called and mockLoading is called, it will only see the instance of state that existed at the time it was called. So any changes to the variable will never be seen by mockLoading. This is why effects have dependencies. That way when a dependency updates, you’ll see it. But I don’t think having a dependency here will help given how your code is structured. I’m not 100% clear on your final goal, but to accomplish what you want based on the code you submitted, you’ll want to useRef instead of useState. useRef gives you an object who’s value is always current. See:

const loadingRef = useRef(false)

function mockLoading () {
    loadingRef.current = true;
    console.log('1', loadingRef.current)
    setTimeout(() => {
        loadingRef.current = false;
      console.log('2', loadingRef.current)
    }, 3000)
}

useEffect(() => {
    mockLoading()
}, []);

Using refs is generally frowned upon unless you absolutely need it. Try to refactor your code to have a dependency on loading in your useEffect call. Though keep in mind you may end up with an infinite loop if your mockLoading always updates loading when it’s called. Try to only update loading if it’s not already set to the value you want. If your end goal is to just update loading after 3 seconds, try this:

const [loading,setLoading] = useState(false);

useEffect(() => {
    setTimeout(() => {
        setLoading(true);
    },3000);
},[]);

return <span>{loading ? 'Loading is true!' : 'Loading is false!'}</span>;

If you want to inspect the value of loading without rendering it, then you’ll need another useEffect with a dependency on loading:

const [loading,setLoading] = useState(false);

useEffect(() => {
    setTimeout(() => {
        setLoading(true);
    },3000);
},[]);

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

return <span>{loading ? 'Loading is true!' : 'Loading is false!'}</span>;

Hope this helps.

Method 2

Declare your handler inside useEffect and log outside

const [loading, setLoading] = useState(false)

  useEffect(() => {
      function mockLoading () {
         setLoading(true)
         console.log('1', loading)
         setTimeout(() => {
         setLoading(false)
         console.log('2', loading)
       }, 3000)
  }
    mockLoading()
  }, []);

  console.log(loading)

Also, you should clean your timeout on unmount

useEffect(() =>{
    const timeout = setTimeout(() =>{}, 0)

    return () => clearTimeout(timeout)
},[])

Method 3

Setting state in React is async, so you won’t see the changes to the state straight after you made them. To track state changes you need to use the effect hook:

const [loading, setLoading] = useState(false);

function mockLoading() {
  setLoading(true);
  console.log("1", loading);
  setTimeout(() => {
    setLoading(false);
    console.log("2", loading);
  }, 3000);
}

useEffect(() => {
  mockLoading();
}, []);

// Display the current loading state
useEffect(() => {
  console.log("loading", loading);
}, [loading]);

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply