I'm building a new React app, on top of an existing boilerplate. It uses lazy loading, combined with React.Suspense.
Problem is, that like in most React apps, i need to fetch some initial meta data from the server, each time the app loads. Let's call it "getAppMetaData".
So what is the problem? The problem is, that while getAppMetaData is pending, I need to present some loader/spinner. And this is exactly what React.Suspense does: It shows the "fallback" UI. Of course i could run a separate loader(which can actually be the same as the fallback UI), but this creates a UX problem, where the animation of the loader "restarts", between the procedures.
So, the question is, how can i "integrate" other async actions, into this suspension? In simple words: "My fallback UI is already showing, while the chunk(from lazy loading) is loaded- so how do i make it also wait for getAppMetaData?"
This is my router:
<ErrorBoundary>
<Suspense fallback={<div className={styles.loader}><Loader /></div>}>
<Switch>
<ProtectedRoute exact component={Home} path="/">
</ProtectedRoute>
<Route path="/lesson">
<Lesson></Lesson>
</Route>
<Route exact path="/login">
<Login />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="*">
<NotFound />
</Route>
</Switch>
</Suspense>
</ErrorBoundary>
React documentation states, that Relay library should be used for this, but i do not want to use any special library for my API calls, just to overcome this simple. It also states:
What If I Don’t Use Relay? If you don’t use Relay today, you might have to wait before you can really try Suspense in your app. So far, it’s the only implementation that we tested in production and are confident in.
All i need is to integrate one little initial API call, into this procedure. How can it be done? Any suggestions will be greatly appreciated.
I would move the Suspense's children into a new component and read the data from this component.
The data will be loaded using a custom function fetchData
which will create the promise and return a object containing a read
method which will throw the promise if the data is not ready.
function fetchData() {
let status = 'pending';
let result;
const promise = fetch('./data.json')
.then(data => data.json())
.then(r => {
status = 'success';
result = r;
})
.catch(e => {
status = 'error';
result = e;
});
return {
read() {
if (status === 'pending') {
throw promise;
} else if (status === 'error') {
throw result;
} else if (status === 'success') {
return result;
}
}
};
}
const dataWrapper = fetchData();
function AppBody() {
const data = dataWrapper.read();
// you can now manipulate the data
return (
<Switch>
<ProtectedRoute exact component={Home} path="/"/>
<Route path="/lesson" component={Lesson} />
<Route exact path="/login" component={Login} />
<Route path="/about" component={About} />
<Route path="*" component={NotFound} />
</Switch>
)
}
function App() {
return (
<ErrorBoundary>
<Suspense fallback={<div className={styles.loader}><Loader /></div>}>
<AppBody/>
</Suspense>
</ErrorBoundary>
);
}
Here is a stackblitz example
This code is inspired by this codesandbox from the react documentation