'How to lazy load multiple components at a same time
I have 2 components, I want to load all of them using lazy load something like
const A = lazy(() => import("../test/A"));
const B = lazy(() => import("../test/B"));
This will create 2 separate bundles, and will import them when required.
But, I want to create a single bundle and when that bundle loads I should be able to use both above components.
I also don't want to create a single component containing both the above components as I want a separate route for both of them
I tried to do something like this https://codesandbox.io/s/eager-raman-mdqzc?file=/src/App.js
Can please somebody will explain me Is this type of functionality possible, If yes then how and what am I doing wrong
Solution 1:[1]
There are probably some tuning options in the code splitter that might better accomplish what you are trying to achieve. But if you don't want to mess around with those (or they are not available to change because you are using a preset configuration), then perhaps you can combine the modules into a single file, and lazy load that "combo module".
To do that, we first need to know how lazy determines what component in a module to load, and what types of objects it expects. From the docs:
The
React.lazyfunction lets you render a dynamic import as a regular component.
React.lazytakes a function that must call a dynamicimport(). This must return aPromisewhich resolves to a module with a default export containing a React component.The lazy component should then be rendered inside a
Suspensecomponent, which allows us to show some fallback content (such as a loading indicator) while we’re waiting for the lazy component to load.You can place the
Suspensecomponent anywhere above the lazy component. You can even wrap multiple lazy components with a singleSuspensecomponent.
So according to that, if you want to use lazy() to wrap the module, then you have to have a component as the default property of the module. So it won't allow you to automatically use a module that uses named exports as a component. However, you can easily make a promise that transforms a named export to a default export, and wrap that in lazy:
// in comboModule.js:
export A from '../test/A'
export B from '../test/B'
// in the code that needs lazy modules
const A = lazy(() => import('./comboModule').then((module) => ({default: module.A})))
const B = lazy(() => import('./comboModule').then((module) => ({default: module.B})))
Note that we have to call import inside the initializer function passed to lazy, or the import will start immediately. Part of lazy's benefit is that is lets you wait until the parent component renders the lazy component before loading. However, import() should cache the result from the first import, and only load the code once.
In the initializer function, we use then to transform the result of import() from something like Promise({A: <Component>, B: <Component>}) to what lazy expects from the initializer function: Promise({default: <Component>})
Now we have two lazy components that both source from one module file.
Resources:
- React code splitting
- import/export
Promise.prototype.then(thenreturns a promise)
Solution 2:[2]
You can use Suspense for waiting both of them. There are two bundles, but you can wait of loading both of them
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</Suspense>
</Router>
);
Solution 3:[3]
I wrote a utility function that generalizes what @garrett-motzner said in his answer:
import {lazy} from 'react';
export const multiLazy = moduleLoaderArray => {
const promises = Promise.all(moduleLoaderArray.map(loader => loader()));
return moduleLoaderArray.map((m, index) =>
lazy(() => promises.then(results => results[index]))
);
}
Works like a charm like this:
const [A, B] = multiLazy([
() => import("../test/A"),
() => import("../test/B"),
]);
So no need to create an intermediary component file (besides for the utility function of course)
I chose this syntax to stick as close as possible to the React.lazy one, but it could be modified to get multiLazy(['../test/A', '../test/B']) if preferred and often used.
Solution 4:[4]
You can use a proxy wrapper to the import statement to give React.lazy what it wants:
const bundle = new Proxy(import("../CombineModule"),{
"get":function (esm,key){
return esm.then(function (m){
return {"__esMODULE":true,"default":m.default[key]};
});
}
});
var CompA = React.lazy(() => bundle.A)
var CompB = React.lazy(() => bundle.B)
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | IT's Bruise |
| Solution 3 | Augustin Riedinger |
| Solution 4 | zcaudate |
