Bundling
Most React apps will have their files/modules bundled using tools like webpack. This bundle can then be included on a webpage to load an entire app at once.
Bundling is the process of following imported files and merging them into a single file: a bundle.
Let's build a small IMDB-like app component to demonstrate the problem.
npx create-react-app my-app
cd my-app
npm start
Synopsis.js
import React from "react";
const Synopsis = () => {
return (
<p>
A thief who steals corporate secrets through the use of dream-sharing
technology is given the inverse task of planting an idea into the mind of
a C.E.O.
</p>
);
};
export { Synopsis };
Plot.js
import React from "react";
const Plot = () => {
const styles = {
container: {
width: "75%",
marginTop: "15px",
},
moveiGif: {
float: "left",
marginRight: "21px",
},
};
return (
<div style={styles.container}>
<img
src="https://i.gifer.com/TZQY.gif"
alt="inception"
height="300"
width="250"
style={styles.moveiGif}
/>
Dom Cobb is a skilled thief, the absolute best in the dangerous art of extraction,
stealing valuable secrets from deep within the subconscious during the dream
state, when the mind is at its most vulnerable. Cobb's rare ability has made
him a coveted player in this treacherous new world of corporate espionage,
but it has also made him an international fugitive and cost him everything
he has ever loved. Now, Cobb is being offered a chance at redemption. One last
job could give him his life back but only if he can accomplish the impossible,
inception. Instead of the perfect heist, Cobb and his team of specialists have
to pull off the reverse: their task is not to steal an idea but to plant
one. If they succeed, it could be a perfect crime. But no amount of careful
planning or expertise can prepare the team for the dangerous enemy that seems
to predict their every move. An enemy that only Cobb could have seen coming.
<hr />
—Warner Bros. Pictures
</div>
);
};
export default Plot;
index.js
import React from "react";
import { render } from "react-dom";
import { Synopsis } from "./Synopsis";
import Plot from "./Plot";
const Movie = () => {
const [showPlot, setShowPlot] = React.useState(false);
return (
<>
<h2>Inception</h2>
<Synopsis />
<button onClick={() => setShowPlot((prevState) => !prevState)}>
{showPlot ? "Hide" : "Show full plot.."}
</button>
{showPlot && <Plot />}
</>
);
};
const rootElement = document.getElementById("root");
render(<Movie />, rootElement);
The app just works fine. But there is room for improvement in terms of performance. What if I have a list of 100 movies and each plot component has ever-growing content and high-pixel assets?
We can load JavaScript (module/code) related to these components lazily or on-demand.
React.lazy
The React.lazy
function lets us render a dynamic import
as a regular component.
Because the import is inside of a function passed to lazy()
, the loading of the component won’t happen until we actually use the component.
Ex,
// SomeComponent must be Default export, like - export default SomeComponent;
// If we use the Named export it fails, like export { SomeComponent }
const LazyComponent = React.lazy(() => import("./SomeComponent"));
const App = () => {
return (
<div>
<LazyComponent />
</div>
);
};
React.lazy
takes a function that must call adynamic import()
.
This must return aPromise
which resolves to a module with a default export containing a React component.
Suspense
If the module containing the OtherComponent
is not yet loaded by the time the app renders, we must show some fallback content while we’re waiting for it to load, such as a loading indicator. This is done using the Suspense
component.
const LazyOtherComponent = React.lazy(() => import("./OtherComponent"));
const App = () => {
return (
<div>
<React.Suspense fallback={<div>Loading...</div>}>
<LazyOtherComponent />
</React.Suspense>
</div>
);
};
Now, the Movie app can be rewritten like below with lazy loading or code splitting. Synopsis.js
and Plot.js
do not need to be changed.
index.js; updated,
import React from "react";
import { render } from "react-dom";
import { Synopsis } from "./Synopsis";
const LazyPlot = React.lazy(() => import("./Plot"));
const Movie = () => {
const [showPlot, setShowPlot] = React.useState(false);
return (
<>
<h2>Inception</h2>
<Synopsis />
<button onClick={() => setShowPlot((prevState) => !prevState)}>
{showPlot ? "Hide" : "Show full plot.."}
</button>
{showPlot && (
<React.Suspense fallback={<div>Loading</div>}>
<LazyPlot />
</React.Suspense>
)}
</>
);
};
const rootElement = document.getElementById("root");
render(<Movie />, rootElement);
Output,
- Observe in the dev tools: click on the "Show plot..." button of the movie app, it downloads the code from the server and caches it for any future call.
- Again, click as many times as you want on "Show plot...". We will not see a network call and all the assets will be cached at the browser level.
Like this, we can lazy load any component that is not needed in initial loading, like the exception handling component, modal dialog components, etc.
React.lazy
currently only supports default exports.
The source code available here - react-code-split