3

I am working with React Hooks and i am trying to update state then do something async and then do someting based on state. This doesn't work since the update state is not available inside the async function. How could you solve such a task using react hooks?

Demo

I basically tried to change the scope of the function but apparently its all immuteable which means the refrences inside the async functions points to old state.

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

// Constants
function somethingAsync(time) {
  return new Promise(resolve => setTimeout(resolve, time));
}

function App() {
  const [loading, setLoading] = React.useState(false);

  const doSomethingAsync = async () => {
    setLoading(true);
    await somethingAsync(2000);
    if (loading) {
      setLoading(false);
    }
  };
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <p>Current Status: {loading ? "Loading" : "Not Loading"}</p>
      <button onClick={doSomethingAsync}>Do Something Async</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

I would like the loading flag to be reset back to false everytime the async function is done and this should be done based on state the have been updated while doing the async function. Right now it only works every second time due to the old references in the async scope.

1
  • I didn't look closely in your code, but I would use useEffect hook if I want to execute something based on state changes Commented Aug 2, 2019 at 6:16

2 Answers 2

3

Just remove the if around your setLoading(false) call.

If you don't, that function will access a stale loading status. Because when that function was created, loading was false. So, after you run your async function, after the await, your function will resume, and even though loading is true it will see it as false. But you'll know that it will be true because you just set it and your App has re-rendered at the await statement. See the behavior below:

CodeSandbox

enter image description here

import React from "react";
import ReactDOM from "react-dom";

import "./styles.css";

// Constants
function somethingAsync(time) {
  return new Promise(resolve => setTimeout(resolve, time));
}

function App() {
  const [loading, setLoading] = React.useState(false);
  console.log("App rendering...");

  const doSomethingAsync = async () => {
    setLoading(true);
    console.log("Before await...");
    await somethingAsync(2000);
    console.log("After await...");
    setLoading(false);
  };
  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      <p>Current Status: {loading ? "Loading" : "Not Loading"}</p>
      <button onClick={doSomethingAsync}>Do Something Async</button>
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Sign up to request clarification or add additional context in comments.

1 Comment

Alright, but i my case what if i need the updated state :-) ? Let's say that something is updated and i need the updated value in my async function. Is there any way to achive this :-) ?
1

You just need to remove the if condition in your async function:

  const doSomethingAsync = async () => {
      setLoading(true);
      await somethingAsync(2000);
      setLoading(false);      
    };

2 Comments

Alright, but i my case what if i need the updated state :-) ? Let's say that something is updated and i need the updated value in my async function. Is there any way to achive this :-) ?
You need to look into useEffect for that: reactjs.org/docs/hooks-effect.html

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.