ReactJS - Code-Splitting



Bundling is one of the important stage in a front end application. It bundles all the front end code along with dependency into one large bundle (bundle.js). The final bundle is size optimized by the bundler. Some of the popular bundler are webpack, parcel and rollup. The final bundle will be fine in most cases. If the final bundled code is big, then the bundler can be instructed to bundle the code into multiple item instead of single, big chunk.

Let us learn how to hint the bundler to split the code and bundle it separately.

Dynamic import

Dynamic import instruct the bundler to split the code. Dynamic import is basically fetching the required module based on the necessity. The code to do normal is as shown below −

import { round } from './math';
console.log(round(67.78));

The same code can be imported dynamically as shown below −

import("./math").then(math => {
  console.log(math.round(67.78);
});

React Lazy component

React provides a function, React.lazy to dynamically import a component. Normally, as we know, a react component will be imported as shown below −

import MyComponent from './MyComponent';

To import the above component dynamically using React.lazy() function is as shown below −

const MyComponent = React.lazy(() => import('./MyComponent'));
The imported component should be wrapped into a Suspense component to use it in the application.
import React, { Suspense } from 'react';
const MyComponent = React.lazy(() => import('./MyComponent'));
function App() {
   return (
      <div>
         <Suspense fallback={<div>Loading...</div>}>
            <MyComponent />
         </Suspense>
      </div>
   );
}

Suspense component is used to load a temporary UI during the loading of the original component. Suspense component includes a fallback props to specify the fallback UI. The fallback UI can be any React element. Sometimes, the dynamic component may fail to load due to network issue or code error. We can use error boundary to handle those situation as shown below −

import React, { Suspense } from 'react';
import MyErrorBoundary from './MyErrorBoundary';
const MyComponent = React.lazy(() => import('./MyComponent'));
const AnotherComponent = () => (
   <div>
      <MyErrorBoundary>
         <Suspense fallback={<div>Loading...</div>}>
            <section>
               <MyComponent />
            </section>
         </Suspense>
      </MyErrorBoundary>
   </div>
);

Here,

  • MyErrorBoundary is wrapped around the Suspense component.

  • If there is any error in loading MyComponent, then MyErrorBoundary handles the error and fallback to the generic UI specified in its component.

One of best scenario to apply the code splitting is routing. Routing can be used to apply the code splitting as shown below −

import React, { Suspense, lazy } from 'react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./routes/Home'));
const About = lazy(() => import('./routes/About'));
const App = () => (
   <BrowserRouter>
      <Suspense fallback={<div>Loading...</div>}>
         <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/about" element={<About />} />
         </Routes>
      </Suspense>
   </BrowserRouter>
);

Here,

  • All routes (component) are loaded using React.lazy() feature

  • Since all routes (Home and About ) are loaded through dynamic import, each route will load only the necessary component instead of all components during its initialization.

React.lazy() supports only default exports. In React, we can export a component by specifying a dynamic name instead of default keyword as shown below −

export const MyComponent = /* ... */;

To make it usable in React.lazy(), we can reexport the component using default keyword as shown below −

export { MyLazyComponent as default } from "./MyComponent.js";

Then, we can import it as usual,

import React, { lazy } from 'react';
const MyComponent = lazy(() => import("./MyComponent.js"));

Applying lazy loading

Let us create a new react application to learn how to apply code splitting in this section.

First of all, create a new react application and start it using below command.

create-react-app myapp
cd myapp
npm 

Next, open App.css (src/App.css) and remove all CSS classes.

// remove all css classes

Next, create a simple hello component, Hello (src/Components/Hello.js) and render a simple message as shown below −

import React from "react";
class Hello extends React.Component {
   constructor(props) {
      super(props)
   }
   render() {
      return (
         <div>Hello, {this.props.name}</div>
      );
   }
}
export default Hello;

Here, we have used the name props to render the hello message with the given name.

Next, create a simple component, SimpleErrorBoundary (src/Components/SimpleErrorBoundary.js) and render either fallback UI during error or children components as shown below −

import React from "react";
class SimpleErrorBoundary extends React.Component {
   
   constructor(props) {
      super(props);
      this.state = { hasError: false };
   }
   
   static getDerivedStateFromError(error) {
      return { hasError: true };
   }
   
   componentDidCatch(error, errorInfo) {
      console.log(error);
      console.log(errorInfo);
   }
   
   render() {
      if (this.state.hasError) {
         return <h1>Please contact the administrator.</h1>;
      }
      
      return this.props.children;
   }
}
export default SimpleErrorBoundary;

Here,

  • hasError is a state variable initialized with false value.

  • getDerivedStateFromError updates the error state when there is an error.

  • componentDidCatch logs the error into the console.

  • render will render either error UI or children based on the error in the application.

Next, open App component (src/App.js), and load the hello component through React.lazy() as shown below −

import './App.css'
import React, { Suspense, lazy } from 'react';
import SimpleErrorBoundary from './Components/SimpleErrorBoundary';
const Hello = lazy(() => import('./Components/Hello'));

function App() {
   return (
      <div className="container">
         <div style={{ padding: "10px" }}>
            <div>
               <SimpleErrorBoundary>
                  <Suspense fallback="<div>loading...</div>">
                     <Hello name="Peter" />
                  </Suspense>
               </SimpleErrorBoundary>
            </div>
         </div>
      </div>
   );
}
export default App;

Here we have,

  • Imported lazy and Suspense component from the react package.

  • Used Hello component by wrapping it with Suspense and SimpleErrorBoundary component.

Finally, open the application in the browser and check the final result. The lazy load does not have have any visible changes in the front end. It will render the hello component in usual manner as shown below −

Applying Lazy Loading

Summary

Code spitting will help to optimize a large application by loading only the necessary component used in the particular page. Suspense and error boundary component can be used to handle the unexpected error while dynamically loading the component.

Advertisements