Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any way to use async await in SetState

I'm 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 if is there any way I can do it properly

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

Edit: It's hard for me to get data before setState because If I only want to get data If the newValue satisfies a condition so Get data force to inside setState

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

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

  const run = async () => {
    setValue((oldValue) => {
      const newValue = [...oldValue];
      // do something makes newValue changes
      if (newValue == true) { // if newValue satisfy a condition
      const res = fetch(`https://fakestoreapi.com/products/${newValue.length}`);
      const result = res.json();
      newValue.push(result.title);
      }
      return newValue;
    });
  };

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

like image 687
Nghĩa Phạm Avatar asked Jan 29 '26 17:01

Nghĩa Phạm


2 Answers

Why 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)}
like image 104
Gabriele Petrioli Avatar answered Feb 01 '26 06:02

Gabriele Petrioli


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.

like image 25
José Ramírez Avatar answered Feb 01 '26 07:02

José Ramírez



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!