Home > Blockchain >  Is there any way to use async await in SetState
Is there any way to use async await in SetState

Time:10-09

Im trying to SetState but inside it I have to get some data that needs async/await and I know it can't be done so I wonder is there any way I can do it properly

codesanbox: https://codesandbox.io/s/infallible-mendeleev-6641iw?file=/src/App.js:0-614

import "./styles.css";
import { useEffect, useState } from "react";

export default function App() {
  const [value, setValue] = useState([]);

  const run = async (x) => {
    setValue((oldValue) => {
      const newValue = [...oldValue];
      const res = fetch(`https://fakestoreapi.com/products/${x}`);
      const result = res.json();
      console.log(result.title);
      return newValue;
    });
  };

  return (
    <div className="App">
      <button onClick={run(2)}>get result</button>
      {value.map((item, index) => {
        return <h2 key={index}>{value[index]}</h2>;
      })}
    </div>
  );
}

CodePudding user response:

We don't you run first the async code, and when the data are available set the state ?

const run = async (x) => {
  const res = await fetch(`https://fakestoreapi.com/products/${x}`);
  const result = await res.json();
  setValue((oldValue) => {
    // you have access to the fetched data here
    const newValue = [...oldValue];
    console.log(result.title);
    return newValue;
  });
};

And ofcourse the click handler should be

onClick={() => run(2)}

CodePudding user response:

You can fetch the data before calling the setState function. Because your fetch call is not depending on anything in the state, but just on the x var that you have avaliable outside of the setState.

So simply call it before calling setState , and once you have it, you'll call the setState.

const run = async (x) => {
     const res = await fetch(`https://fakestoreapi.com/products/${x}`);
     const result = await res.json();
    
     setValue((oldValue) => {
          const newValue = [...oldValue];
          console.log(result.title);
          return newValue;
   });
  };
      

And you have a mistake when you call your run() function in the onClick event, you should change it to this:

<button onClick={() => run(2)}>get result</button>

CodePudding user response:

There's a React component called Suspense. I think it first appeared in v16, but in all honesty I have only used it with React v18, so unsure if it will work for you.

I'll refer you to a live demo I have: wj-config Live Demo

Here I use <Suspense> to wrap a component that requires data that is asynchronously obtained, just like when you use fetch().

Suspense works like this:

  1. It attempts to load the inner children but places a try..catch in said loading process.
  2. If an error is caught, and the caught error is a promise then Suspense will instead render the component in its fallback property.
  3. After rendering what's in fallback, React awaits the caught promise.
  4. Once the caught promise resolves, Suspense retries rendering the child components.

I hear there are frameworks that provides the necessary mechanisms to use Suspense in a simple and expedite manner, but in my live demo I did it all myself. Is not too bad I think.

The procedure to use this is:

  1. Create a readXXX function that is a suspender function (a function that throws a promise).
  2. Call this function at the beginning of your inner Suspense component's code.
  3. Program the rest of your inner component as if the readXXX function has worked and returned the needed data.

Here's the code I have in the live demo to create suspender functions:

function suspenderWrapper(promise) {
  let isPending = true;
  let outcome = null;
  const suspender = promise.then(
    (r) => {
      outcome = r;
      isPending = false;
    },
    (e) => {
      outcome = e;
      isPending = false;
    }
  );
  return () => {
    if (isPending) {
      throw suspender;
    }
    return outcome;
  };
}

Open up my live code's demo and look for App.js where all the magic is shown.

  • Related