Code Splitting and Lazy Loading with react

 Code splitting and lazy loading are two techniques that can improve the performance and user experience of React applications. Code splitting allows you to divide your JavaScript code into smaller chunks that can be loaded on demand, reducing the initial loading time and bandwidth usage. Lazy loading enables you to defer loading some components or resources until they are actually needed, saving memory and CPU cycles.

In this blog post, I will show you how to use React’s built-in features, such as React.lazy and Suspense, to implement code splitting and lazy loading for your React components. I will also show you how to use a third-party library called react-loadable to achieve the same results with more flexibility and features.


Using React.lazy and Suspense

React.lazy is a function that lets you render a dynamic import as a regular component. Dynamic imports are a way of code-splitting, which is central to lazy loading. For example, suppose you have a component called Profile that is only used in one route of your app. You can use React.lazy to import it dynamically and load it only when the route is rendered:

import React, { lazy } from 'react';

// Import Profile component dynamically

const Profile = lazy(() => import('./Profile'));

function App() {

  return (

    <div className="App">

      <Router>

        <Switch>

          <Route path="/profile" element={<Profile />} />

          {/* Other routes */}

        </Switch>

      </Router>

    </div>

  );

}


export default App;

Notice that the Profile component is wrapped with a lazy function that takes a function that returns a dynamic import as an argument. This way, the Profile component will be loaded only when the /profile route is matched.

However, using React.lazy alone is not enough. You also need to use the Suspense component to handle the loading state of the lazy component. The Suspense component accepts a fallback prop that specifies what to render while the lazy component is being loaded. For example, you can use a spinner or a placeholder as the fallback:

import React, { lazy, Suspense } from 'react';

import { Spinner } from './Spinner';

// Import Profile component dynamically

const Profile = lazy(() => import('./Profile'));

function App() {

  return (

    <div className="App">

      <Router>

        <Switch>

          <Route path="/profile" element={

            // Use Suspense to handle the loading state

            <Suspense fallback={<Spinner />}>

              <Profile />

            </Suspense>

          } />

          {/* Other routes */}

        </Switch>

      </Router>

    </div>

  );

}


export default App;

Now, when the /profile route is rendered, the Spinner component will be shown until the Profile component is loaded and ready to be displayed.

You can use React.lazy and Suspense to code-split and lazy load any component in your app, not just route components. For example, you can use them to load a modal, a sidebar, a chart, or any other component that is not essential for the initial rendering of the app.

Using react-loadable

react-loadable is a library that provides a higher-order component for loading components with dynamic imports. It offers more features and options than React.lazy and Suspense, such as error handling, server-side rendering, and loading multiple resources. You can install it with npm or yarn:

npm install react-loadable

# or

yarn add react-loadable

To use react-loadable, you need to import the Loadable function from the library and pass it an object with the following properties:

  • loader: a function that returns a dynamic import of the component to be loaded.

  • loading: a component that renders the loading state while the component is being loaded.

  • error: an optional component that renders the error state if the component fails to load.

  • delay: an optional number that specifies the minimum time (in milliseconds) to wait before showing the loading component. The default is 200 ms.

  • timeout: an optional number that specifies the maximum time (in milliseconds) to wait before showing the error component. The default is no timeout.

For example, to use react-loadable to load the Profile component, you can do something like this:

import React from 'react';

import Loadable from 'react-loadable';

import { Spinner } from './Spinner';

import { Error } from './Error';


// Use Loadable to load Profile component dynamically

const Profile = Loadable({

  loader: () => import('./Profile'),

  loading: Spinner,

  error: Error,

  delay: 300,

  timeout: 10000

});


function App() {

  return (

    <div className="App">

      <Router>

        <Switch>

          <Route path="/profile" element={<Profile />} />

          {/* Other routes */}

        </Switch>

      </Router>

    </div>

  );

}


export default App;

Now, when the /profile route is rendered, the Spinner component will be shown after 300 ms of delay, and the Error component will be shown if the Profile component fails to load after 10 seconds of timeout.

You can also use react-loadable to load multiple resources at once, such as images, stylesheets, or data. To do this, you need to use the Loadable.Map function instead of the Loadable function, and pass it an object with the following properties:

  • loader: an object that maps keys to functions that return dynamic imports of the resources to be loaded.

  • loading: a component that renders the loading state while the resources are being loaded.

  • error: an optional component that renders the error state if any of the resources fails to load.

  • delay: an optional number that specifies the minimum time (in milliseconds) to wait before showing the loading component. The default is 200 ms.

  • timeout: an optional number that specifies the maximum time (in milliseconds) to wait before showing the error component. The default is no timeout.

  • render: a function that receives the loaded resources as an object and returns the component to be rendered.

For example, to use react-loadable to load the Profile component along with an image and some data, you can do something like this:

import React from 'react';

import Loadable from 'react-loadable';

import { Spinner } from './Spinner';

import { Error } from './Error';


// Use Loadable.Map to load multiple resources dynamically

const Profile = Loadable.Map({

  loader: {

    // Load Profile component

    Profile: () => import('./Profile'),

    // Load image

    image: () => import('./avatar.jpg'),

    // Load data

    data: () => fetch('/api/profile').then(res => res.json())

  },

  loading: Spinner,

  error: Error,

  delay: 300,

  timeout: 10000,

  // Render the component with the loaded resources

  render(loaded, props) {

    const Profile = loaded.Profile.default;

    const image = loaded.image.default;

    const data = loaded.data;

    return <Profile image={image} data={data} {...props} />;

  }

});


function App() {

  return (

    <div className="App">

      <Router>

        <Switch>

          <Route path="/profile" element={<Profile />} />

          {/* Other routes */}

        </Switch>

      </Router>

    </div>

  );

}


export default App;

Now, when the /profile route is rendered, the Spinner component will be shown after 300 ms of delay, and the Error component will be shown if any of the resources fails to load after 10 seconds of timeout. The Profile component will be rendered with the loaded image and data as props.

Conclusion

In this blog post, I have shown you how to use code splitting and lazy loading to improve the performance and user experience of your React applications. You can use React.lazy and Suspense to load components on demand with minimal effort, or use react-loadable to load components and resources with more features and options. Either way, you can reduce the initial loading time and bandwidth usage of your app, and provide a smoother and faster experience for your users. 🚀












0 Comments