Skip to content Skip to sidebar Skip to footer

Async Function In React Doesn't React To State Change

I want my aysnc function to react to state change so that it can terminate upon state change. What I have discovered is that it doesn't react to changing the state. In the provided

Solution 1:

State variables never change their values. (Note that you can, have, and should declare them with const because they don't change).

Subsequent invokes of the component function will get a new state variable with the same name and a different value.

Your testFunction function has closed over the old state variable.


I suspect you could solve this with a reference:

const [testVariable, setTestVariable] = useState(false);
const reference = useRef();
reference.current = testVariable;

… and then have your function test the value of reference.current instead of testVariable.


This does, however, feel like an XY Problem where the real solution is "Use the return value from the function you pass to useEffect to stop whatever it is you want to stop" (and use testVariable as a dependency of useEffect).

Solution 2:

state update in react is async means that your state will be changed in next render cycle. you can useEffect hook to track if any state is changed or not.

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

Solution 3:

Change it to

const [testVariable, setTestVariable] = useState(false);

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

const testFunction = async () => {
  awaittimeout(1000);

  console.log("Setting test variable to: " + true);

  let newTestVariable = true;  
  setTestVariable(newTestVariable);

  awaittimeout(1000);
  
  console.log("Test variable is: " + newTestVariable);
}

Changing the state variable is an asynchronous operation and works with something called the event loop. It will not immediately get updated when you did setTestVariable(true).

Solution 4:

The async functionis changing the state, but you're logging testVariable in the same lifecycle, which is why it looks as though the state wasn't changed.

If you instead change the code to be

const [testVariable, setTestVariable] = React.useState(false)

console.log('testVariable', testVariable)
React.useEffect(() => {
  testFunction()
}, [])

const testFunction = async () => {
  awaittimeout(1000)
  setTestVariable(!testVariable)
}

return<h2>{testVariable}</h2>

It will log the output:

testVariable false
testVariable false
testVariable true
testVariable true

The reason it's showing two logs for both false and true is because the console.log is outside the React.useEffect(() => {}, []) (which is equivalent to componentDidMount for stateful React components). React is first executing the console.log before the component mounts, then after the component mounts it executes the code inside React.useEffect and rerenders the component, thus executing the console.log statement again.

To only log once you would have to change the React.useEffect to:

React.useEffect(() => {
  console.log('testVariable', testVariable)
  testFunction()
}, [testVariable])

Adding a properties to the array arguments [testVariable], changes the React.useEffect from being a componentDidMount equivalent function to be a function chat executes on componentDidMount and for every new state of testVariable.

Post a Comment for "Async Function In React Doesn't React To State Change"