Reputation: 332
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>
);
}
Upvotes: 2
Views: 4537
Reputation: 196236
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)}
Upvotes: 2
Reputation: 1102
You can get the old value from the value
variable which is always storing the current state, and do the if
check on it instead.
And if the if
condition on value
was true - then you can call fetch
and after that call setState
and update the state.
If the condition was not true, there is no need to update the state since it stayed the same. See the code below:
const [value, setValue] = useState([]);
const run = async () => {
//some condition on the current value
if (value) {
const res = await fetch( `https://fakestoreapi.com/products/${newValue.length}`);
const result = await res.json();
// And here apply the changes on the state
setValue((oldValue) => {
const newValue = [...oldValue];
newValue.push(result.title);
return newValue;
});
}
//Outside the if block - no need to change the state.
};
Upvotes: 1
Reputation: 2380
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:
try..catch
in said loading process.fallback
property.fallback
, React awaits the caught promise.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:
readXXX
function that is a suspender function (a function that throws a promise).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.
Upvotes: 1