-1

With class-based components, I can (for example) do this:

doThing = () => {
    this.setState({
        test: 'Zest'
    })

    setTimeout(() => {
        console.log(this.state.test) // will return 'Zest'
    },1000)

}

However, in a functional component with the useState hook, the state won't be updated until another event listener is called. My example is most obvious when using setTimeout listener, but it shows up in lots of other contexts too.

Once an event listener is called, no state updates are received until another one's called.

Up until now, every time this weird quirk has caused any issues I just change the component back to a class-based one, but I've always wondered what it was about.

Can anyone explain why or offer a means of getting an updated state within a callback/timeout/interval/etc using a functional component and react hooks?

Shane Creedon
  • 1,573
  • 1
  • 14
  • 18
MitchEff
  • 1,417
  • 14
  • 29
  • Does this answer your question? https://stackoverflow.com/questions/60789246/react-why-using-debounce-and-setstate-in-the-same-callback-not-work – keikai Mar 28 '20 at 12:33
  • sounds related to [useState set method not reflecting change immediately](https://stackoverflow.com/q/54069253/1176601) - but please say what you are trying to achieve, list the code that does NOT work for you and what is expected behaviour – Aprillion Mar 28 '20 at 13:11

2 Answers2

0

I'm not sure what your problem is. Is it related to setTimeout? Because there is a gotcha in react hooks for setTimeout (and setInterval). setTimeout is a closure, therefore, when setTimeout is scheduled it uses the value of count at that exact moment in time, which is the initial value.

For proper use of setTimeout in hooks, see this question.

For your very simple example, this is the equivalent code using hooks, and it works:

function App() {
  const [test] = useState("Zest");

  setTimeout(() => {
    console.log(test);
  }, 1000);

Could you demonstrate the AddEventlistener problem you are having?

Also, I'm a bit suspicious of the fact that you need to add event listeners in a react program. You can usually get by just using react onChange handlers and suchlike.

Klas Mellbourn
  • 42,571
  • 24
  • 140
  • 158
0

I can't understand why you use setTimeout, maybe you know the this.setState is an asynchronous function and you used a setTimeout to handle an action after the state changed. if it is your goal you should write it like below:

doThing = () => {
  this.setState(
    {
      test: 'Zest'
    },
    () => {
      console.log(this.state.test) // **
    }
  );
}

Actually, based on ReactJS docs, the setState can get a callback. so if you wanna do something right after changing state use the callback function of setState.

NOW on Function component

For having the above situation on a function component you should write just like below:

import React, { useState, useEffect, useCallback } from 'react';

const FunctionComponent = () => {
  const [test, setTest] = useState('');
  const doThing = useCallback(() => {
    setTest('Zest');
  }, []);
  
  useEffect(() => {
    console.log(test) // here is exactly like **
  }, [test]);

  return (
    <div // etc...
  );
};

The doThing function is created just for once at the mounting the FunctionComponent because we use useCallback and pass [] dependency on it. then the callback of useEffect frequently runs just after changing state of test.

Community
  • 1
  • 1
AmerllicA
  • 29,059
  • 15
  • 130
  • 154