Thupi
Thupi

Reputation: 31

How to manage async functions using React Hooks API

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.

Upvotes: 3

Views: 103

Answers (2)

cbdeveloper
cbdeveloper

Reputation: 31335

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);

Upvotes: 3

Clarity
Clarity

Reputation: 10873

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

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

Upvotes: 1

Related Questions