When building our React application, sometimes has to fetch data from somewhere usually from a server using the Fetch api. Normally, I would set up my React application to fetch data like this.
import React, { useState } from 'react;
import LoadingScreen from '../LoadingScreen';
function MyComponent(props) {
const [fetchedData, setFetchedData] = useState(null);
useEffect(() => {
fetch('http://datafromserver.com/data')
.then(res => {
return res.json();
})
.then(data => {
setFetchedData(data);
})
}, [])
return fetchedData ? (<Content {...fetchedData} />) : <LoadingScreen/>
}
Which is fine and works but has some issues
Then another option you might consider would be to just fetch everything at the highest level component. This wont be favorable because
React now comes with the Suspense component that helps alleviate these issues. The suspense component allows us to hold off code from loading until we’ve finished fetching data
First what we want to do is create a Component that will wrap suspense around another component
import React, {Suspense} from 'React'
import ComponentWithFetch from '../ComponentWithFetch
function MyComponent(props) {
return (
<Suspense fallback={<h1>Loading....</h1>}>
<ComponentWithFetch/>
</Suspense>
)
}
Now we want to build out our component that calls an api through fetch.
Lets build out the scaffolding first
import React, {Suspense} from 'React'
function ComponentWithFetch(props) {
const myUrlPath = 'http://myUrlPath.com/data';
function getPromise() {
return fetch(myUrlPath)
.then(res => res.json());
}
function wrappedPromise(promise) {
let status = "pending";
let result;
const suspender = promise.then(
r => {
status = "success";
result = r;
},
e => {
status = "error";
result = e;
});
return {
read() {
if (status === "pending") {
throw suspender;
} else if (status === "error") {
throw result;
} else if (status === "success") {
return result;
}
}
};
}
useEffect(() => {
}, [])
return (
<div>
{wrappedPromise(getPromise()).read()}
</div>
)
}
This is a lot to unpack, so lets start with the getPromise function.
function getPromise() {
return fetch(myUrlPath)
.then(res => res.json());
}
This function returns the promise we want to run. In this case that promise will be a fetch function. The call to .then appended to the fetch function returns the response in a format we can use.
The function we want to look at is wrappedPromise
function wrappedPromise(promise) {
let status = "pending";
let result;
const suspender = promise.then(
r => {
status = "success";
result = r;
},
e => {
status = "error";
result = e;
});
return {
read() {
if (status === "pending") {
throw suspender;
} else if (status === "error") {
throw result;
} else if (status === "success") {
return result;
}
}
};
}
The wrapped promise function pretty much just wraps itself around our chosen promise and gives us the status of our promise if the promise is still loading , we give back the unresolved promise, and if the promise inside is done we give back the result of the promise.