I am going through react official docs to understand about react lazy.
As per docs, https://reactjs.org/docs/code-splitting.html#reactlazy
This will automatically load the bundle containing the OtherComponent when this component gets rendered.
React.lazy
takes a function that must call a dynamicimport()
. This must return aPromise
which resolves to a module with a default export containing a React component.Normal import
import OtherComponent from './OtherComponent'; function MyComponent() { return ( <div> <OtherComponent /> </div> ); }
Lazy import
const OtherComponent = React.lazy(() => import('./OtherComponent')); function MyComponent() { return ( <div> <OtherComponent /> </div> ); }
But I couldn’t understand much from the docs about the advantage of using lazy for importing the component. Also Why promise comes into picture for importing modules/components?
Single Page Applications (SPAs) typically work by downloading a bundle that contains all the scripts your app needs. Routing and URL changes in the browser's address bar are handled via HTML5 history API. This creates an incredibly powerful user experience since the browser never needs to refresh to load any part of your application - you can navigate from one page/URL to another instantly.
While this sounds good in theory, in practice, things are ugly. One of the biggest problems is the bundle size - in an SPA of medium complexity, the bundle size can easily reach megabytes. Now, such big files not only take a long time to download but also are not cached by the browser - so they need to be fetched again and again which in turn makes your app look sluggish.
There have been many attempts to remedy this - asynchronous and deferred loading of scripts, and code splitting. Code Splitting refers to the technique of splitting your mega bundle into smaller chunks - the idea being that you download a core or critical part of your app as soon as possible and then load rest of the code on demand. This solves the problems mentioned above but achieving code splitting turns out to be incredibly hard.
One of the reason being, that it's hard for tools like Webpack to figure out how to split the code effectively, with less manual intervention. In AMD world, define
/require
helped you define code split points - so you can load critical parts first and load rest on demand in an asynchronous fashion.
In React world, lazy
lets you do so effectively with less manual intervention. By defining something lazy
, you indicate that this part of the code is non-critical and can be loaded behind the scenes, on demand.
The () => import('./OtherComponent')
the syntax is also known as dynamic import which is different from a static import: import OtherComponent from './OtherComponent'
. Dynamic imports are asynchronous and thus they always return a Promise. A Promise ensures that the code that depends on this lazily loaded module, executes only after the script has loaded.
Consider the following snippet:
const PurchaseHistory = lazy(() => import('./components/PurchaseHistory'))
class App extends Component {
state = {
purchases: [ ],
};
componentDidMount() {
fetch(`some/api/that/returns/data`)
.then(res => res.json())
.then(res => {
this.setState({ purchases: res.data });
});
}
render() {
return (
<div className="app">
<Suspense fallback={Loader}>
<PurchaseHistory {...purchases} />
</Suspense>
</div>
);
}
}
In the above code, React can immediately render the app with the Loader
and until the fetch succeeds, it doesn't even need to load the lazy PurchaseHistory
component. This saves download times, reduces memory footprint and saves CPU cycles that would be needed to download and process the PurchaseHistory
component.
Suspense is new API in React 16.6.
Together with another upcoming feature called Concurrent Rendering, the above code ensures that React renders the app pretty fast (faster than React 16). It can do so because React knows which parts are crucial and which parts are not.
For further reading, I suggest this blog post that talks about all 3 features with code examples.
Put it simply, if whichever component you are lazily loading is not used, it's not downloaded in the browser.
In the code below, Hello
& Bye
are imported.
But since only Hello
is used, code for Bye
is not included.
import React, { lazy, Suspense } from "react";
import ReactDOM from "react-dom";
const Hello = lazy(() => import("./components/Hello"));
const Bye = lazy(() => import("./components/Hello"));
function App() {
return (
<Suspense fallback={<>Loading...</>}>
<Hello />
</Suspense>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
You can follow along on CodeSandbox
When you inspect the deployed site on Netlify,
you can see from the site that Bye
is not loaded.
You can take advantage of this by loading components on demand.
Shameless plug: If you want to know more about other use cases, check out my blog entry, Loading React Components Dynamically on Demand using React.lazy.
It lets you lazy-load the other component's script. This is primarily going to be useful if that script is separate from other scripts (e.g., not part of your main bundle).
The advantages are:
Of course, there are disadvantages:
As always, the trade-offs will go one way for some projects and another way for other projects. A large single-page app with lots of sections the user may or may not visit would probably benefit; a small page where all the components are probably rendered early on probably wouldn't.
When your Single Page Application starts growing large. It does not make sense to load all the javascript at once. Let's say you are on Homepage and loading javascript for Profile section wouldn't make sense it will slow your page load for the homepage. So code-splitting your huge application into sizeable chunks will make the application perform better. In short lazy loading
your components.
Basically, for normal import, it imports all the child components needed for a the parent component. But, for Lazy loaded child components, it loads them asynchronously. This really helps save the initial load time for the parent component when the child component itself contains many other hierarchies of child components and ultimately lengthening the time to load of the main page. But, when using Lazyloading for components, the component that you want to lazyload, has to be enclosed with "Suspense" tag with a fallback property as the content of the lazy loaded component will not be available when the parent component loads and the jsx provided in the fallback property will be loaded then.
React Suspense allows you to easily add code-splitting into your application by lazy loading certain components, replacing them with something else until they've download. Before code-splitting, your webpack build might generate a single file for all of your application code. Imagine a React application with multiple pages and components. One of these pages is MyPage.
import React from "react";
import OtherComponent from "./OtherComponent";
const MyPage = () => (
<div>
<h1>I am the page</h1>
<OtherComponent/>
</div>
);
export default MyPage;
Let's lazy load the OtherComponent
so it's code isn't downloaded by the browser until a visitor hits MyPage
for the first time.
import React, { Suspense, lazy } from 'react';
const OtherComponent = lazy(() => import('./OtherComponent');
const MyPage = () => (
<div>
<h1>I am the page</h1>
<Suspense fallback={<p>Loading...</p>}>
<OtherComponent/>
</Suspense>
</div>
);
export default MyPage;
OtherComponent
now gets split into a seperate chunk with a numbered filename in your build directory e.g. 1.js
Better filenames with webpackChunkName
webpackChunkName
in an inline comment, we can instruct webpack to name the file something much friendlier.const OtherComponent = lazy(() => import(/* webpackChunkName: "OtherComponent" */ './OtherComponent');
Now the chunk will be named OtherComponent.js
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With