Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

useEffect not triggered on state update by its dependency

I am trying to implement a seamless login and trigger another function when login is successful

const [token, setToken] = useState();

useEffect(() => {
    async function attemptLogin() {
      await fetch('http://localhost:3000/login')
        .then(response => response.json())
        .then(data => console.log(data.data))
        .then(data => setToken(JSON.stringify(data))) // {data: 'Logged in'}
        .catch(err => {
          console.error('error occured: ', err.message)
        });
    }
    attemptLogin();
  }, []);

useEffect(() => {
  console.log('should run after token update');
  console.log(token); //undefined
  // another fetch goes here since we needed to login to get token for API 
}, [token]);

So the useEffect with the dependency runs before the one used on mounting. why? Shouldn’t it run only when state changes? Does it run on initialization or something? Then why not run when I get the data from my fetch?

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

The problem is with this piece of code:

       .then(data => console.log(data.data))
        .then(data => setToken(JSON.stringify(data)))

In .then(), data is not passed on further to the next .then(). That is why the second .then() becomes something like setToken(JSON.stringify(undefined)).

console.log(JSON.stringify(undefined))

As you see that will return undefined so you are doing setToken(undefined).

You probably want to do .then(data => data.data) instead of .then(data => console.log(data.data)), so you are actually returning something.

Note: Do not need the second .then() because there is nothing async here :

.then(data => setToken(JSON.stringify(data.data)))

Also,

useEffect(() => {
  console.log('should run after token update');
  console.log(token); //undefined
  // another fetch goes here since we needed to login to get token for API 
}, [token]);

the callback in this useEffect will run on the first render (mount) because token is given a value at that time (although undefined). This counts as a change for React, because earlier the variable did not even exist (a codesandbox demonstrating this).

From the docs:

Does useEffect run after every render? Yes! By default, it runs both after the first render and after every update.

You can check for the first mount using a ref if you do not want this to run on first mount or check the value for undefined using if.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading