What we want to achieve?
The components need the data to render. In other words, the components are bind to data. Of course, we can fetch the data in advance, even before the component has loaded or at least before it is rendered. Ask most developers - there is practically no other way, they will say. Indeed, no data to bind with - no sense to waste a time. I would just add - why, in that case, preload a component that has no chance of rendering due to lack of data? In other words, we want to load the component code at the same time as we fetch its data. If the idea of dynamic code loading (code splitting) has not become your daily practice, then here are a few reasons why you should do it today.
We will omit the details of how React implements code-splitting. There are many materials on the web on this subject. It's enough just to remember about React.lazy() and React.Suspense mechanisms.
Briefly, this technique is described by the following excertp:
// App.js
import React, { lazy, Suspense } from 'react';
import { Routes, Route, Link } from 'react-router-dom';
const LazyComponent = React.lazy( () => import('./Resource') )
const App = () => {
return <>
<Link to={'/res'}>Resources</Link>
<Routes>
<Route path='res' element={
<Suspense fallback={'Loading...'}
<LazyComponent />
</Suspense>
}
</Routes>
</>
}
Regarding React.Suspense, note that at time of this writing only two known libraries know how to integrate with Suspense: there are React Relay and SWR. And if so, it is these two that we will deal with here.
Implementation with SWR
SWR was created by Vercel (authors of React.next) and it provides the sophisticated cache support for the data fetching based on HTTP RFC 5861. Let's add its neat mutate() function to fetch some date (without the network for a brevity)
// fetcher.js
const sleep = (ms) => {
return new Promise(resolve, reject) {
setTimeout(resolved, ms)
}
}
export default async(url) => {
await sleep(1000);
return {url};
}
// App.js
import React, { lazy, Suspense } from 'react';
import { Routes, Route, Link } from 'react-router-dom';
+ import fetch from './fetcher';
const LazyComponent = React.lazy( () => import('./Resource') )
const App = () => {
+ const [shouldFetch, setShouldFetch] = React.useState(false);
+ useSWR(shouldFetch? key : null, fetch);
+ const handleClick = (event) => {
+ setShouldFetch(true);
+ }
return <>
<Link to={'/res'}
+ onClick={handleClick}
>Resources</Link>
<Routes>
<Route path='res' element={
<Suspense fallback={'Loading...'}
<LazyComponent />
</Suspense>
}
</Routes>
</>
}
Essentially, we just added here with onClick() handler that will execute in parallel with lazy component loading.
Inside this handler the flag for conditional fetching is set. Its purpose is to call useSWR and store the returned result into cache with passed key. For a meanwhile we don't use this result, it is just stored in cache, but apparently we following the way: two actions - loading Resource component code and data fetching for it - are now executed asynchronously.
Now just let the lazy component to use the data already fetched by the precious call to useSWR. We are going to use useSWR() hook one again inside the component:
// Resource.jsx
const Resource () => {
const { data } = useSWR('/res', fetcher);
}
Implementation with React Relay
- Relay endpoints
What we want to achieve?
const App = () => {
}
Top comments (0)