React.lazy and prefetching components
Asked Answered
C

3

8

I have a 2 step Application Flow that looks like this:

const Step1 = React.lazy(() => import('./Step1'));
const Step1 = React.lazy(() => import('./Step2'));

<Suspense fallback={<Loading />}>
  <Route path="/step1" render={() => <Step1 />} />
  <Route path="/step2" render={() => <Step2 />} />
</Suspense>

Using React.lazy, I can defer loading <Step2 /> while the user is on <Step1 />, which can improve initial page load. However, I would like to prefetch <Step2 /> while the user is on <Step1 /> as an optimization. Is there an API to do this with React.lazy?

Edit:

To elaborate - I'm using a router to render a 2 step form. Initially the user will start on /step1. After the user completes all the tasks in <Step1 /> they will be routed to path /step2. At this point the router will render the <Step2 /> component.

I'm asking if there is a pattern to pre-fetch <Step2 /> while the user is still on <Step1 />.

Callboard answered 4/11, 2019 at 4:56 Comment(5)
can you explain more?Stillmann
I added some more context in an editCallboard
Ok, So first I think there is something to clarify things, lazy just separating the components on different files to be loaded, However, your Step1 & Step2 Component will load almost asynchronously because they are both on the same file that they will be called from in the same timeStillmann
Hey, Eslam you are getting this wrong when we use React.lazy in React Router component, the first component to be load is the component whose route matches. And when we switch to a different route by Link or other methods at that time the other file will be loaded i.e. in route based splitting the file is loaded based on the route. You can try this by simpling having the above code and in the component, you can render 10000 items of array by simply looping over them and you can then see the difference.Cann
You can also check this PR which I came across today morning. This PR is by Michael Jackson who is the co-author of react-router. Do follow this thread to get most out of the conversation. Link: github.com/facebook/react/pull/17245Cann
C
26

I was also reading about this few days back and I liked this approach:

Enhance the React.lazy to have a callback that can be used to load the component. Something like this:

function lazyWithPreload(factory) {
  const Component = React.lazy(factory);
  Component.preload = factory;
  return Component;
}

const ComponentToPreload = lazyWithPreload(() => import("./Component"));

/* usage in Component */

ComponentToPreload.preload(); // this will trigger network request to load the component


In this way, you can preload the component wherever you want. Like on click event or after the current component has Mounted.

Must read the original post: https://medium.com/hackernoon/lazy-loading-and-preloading-components-in-react-16-6-804de091c82d


If you are using react-loadable. You can check this: https://github.com/jamiebuilds/react-loadable#preloading

Hope this helps!

Cann answered 4/11, 2019 at 6:43 Comment(2)
Thanks Yash! I'm having trouble getting lazyWithPreload to work nicely with Typescript. However, I found what I was looking for in that article you linked. By declaring import('./Step2') alongside the React.lazy(() => import('./Step2')), this will fetch the Step2 component in parallel.Callboard
Hey, right you can do that in order to fetch it parallel whereas the Enhanced Lazy function gives more control on how to preload based on events or lifecycle.Cann
N
0

For anyone interested, I added types to Yash Joshi's answer.

import React, { ComponentType, LazyExoticComponent } from "react";

type PreloadableComponent<T extends ComponentType<any>> =
  LazyExoticComponent<T> & {
    preload: () => Promise<void>;
  };

function lazyWithPreload<T extends ComponentType<any>>(
  factory: () => Promise<{ default: T }>
): PreloadableComponent<T> {
  const Component: Partial<PreloadableComponent<T>> = React.lazy(factory);

  Component.preload = async () => {
    await factory();
  };

  return Component as PreloadableComponent<T>;
}

export default lazyWithPreload;
Nicolettenicoli answered 24/2 at 19:48 Comment(0)
J
-1

I wrote a small wrapper function around React lazy to handle missing assets, we can tweak that a little bit to easily add support for prefetching.

// Lazy with Retry and Prefetching
export const lazyWithRetryAndPrefetching = (componentImport) => {
  const factory = async () => {
    try {
      return await componentImport();
    } catch (error) {
      console.error(error);
      return window.location.reload();
    }
  };

  const Component = lazy(factory);

  Component.prefetch = factory;

  return Component;
};

and prefetching on demand is as easy as

// App.jsx
const Material = lazyWithRetryAndPrefetching(() => import("./Material"));

useEffect(() => {
    Material.prefetch();
}, []);

More details are posted on my blogs

Jaclin answered 29/5, 2023 at 3:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.