• Home
  • Code splitting in React App

Code splitting in React App

When you build larger React Apps soon you will realize that when you build your project you will get one very large .js file. This file contains all the JavaScript our app needs. But let say for example if a user is simply loading the login page to sign in; it doesn’t make sense that we load the rest of the app with it. So how can we solve this problem In React there is something called Code Splitting that can help us if we done it right.

Create React App (from 1.0 onwards) allows us to dynamically import parts of our app using the import()proposal. You can read more about it here. While, the dynamic import() can be used for any component in React apps; it works very well with React Router. Since, React Router is figuring out which component to load based on the path; it would make sense that we dynamically import those components only when we navigate to them.

Typical scenario in our React App with React Router will look something like this.

/* Import the components */
import Home from "./containers/Home";
import Posts from "./containers/Posts";
import NotFound from "./containers/NotFound";

/* Use components to define routes */
export default () =>
  <Switch>
    <Route path="/" exact component={Home} />
    <Route path="/posts/:id" exact component={Posts} />
    <Route component={NotFound} />
  </Switch>;

As we see above all imports are static, this means that we request our components on load and regardless of what route user match all components are loaded and available to us. To implement Code Splitting here we are going to want to only load the component that responds to the matched route.

To accomplish this task we are going to create Async Component which will be responsible to load content ( component ) dynamically when user request route in our App. Here is sample code that we can implement in our React App for example to  src/components/AsyncComponent.js.

import React, { Component } from "react";

export default function asyncComponent(importComponent) {
  class AsyncComponent extends Component {
    constructor(props) {
      super(props);

      this.state = {
        component: null
      };
    }

    async componentDidMount() {
      const { default: component } = await importComponent();

      this.setState({
        component: component
      });
    }

    render() {
      const C = this.state.component;

      return C ? <C {...this.props} /> : null;
    }
  }

  return AsyncComponent;
}

We are doing a few things here:

  1. The asyncComponent function takes an argument; a function (importComponent) that when called will dynamically import a given component. This will make more sense below when we use asyncComponent.
  2. On componentDidMount, we simply call the importComponent function that is passed in. And save the dynamically loaded component in the state.
  3. Finally, we conditionally render the component if it has completed loading. If not we simply render null. But instead of rendering null, you could render a loading spinner. This would give the user some feedback while a part of your app is still loading.

Next step is instead of importing statically our example Home component like this

import Home from "./containers/Home";

we are going to use this dynamic import

const AsyncHome = asyncComponent(() => import("./containers/Home"));

It’s important to note that we are not doing an import here. We are only passing in a function to asyncComponent that will dynamically import() when the AsyncHome component is created.

Also, it might seem weird that we are passing a function here. Why not just pass in a string (say ./containers/Home) and then do the dynamic import() inside the AsyncComponent? This is because we want to explicitly state the component we are dynamically importing. Webpack splits our app based on this. It looks at these imports and generates the required parts (or chunks). This was pointed out by @wSokra and @dan_abramov.

We are then going to use the AsyncHome component in our routes. React Router will create the AsyncHomecomponent when the route is matched and that will in turn dynamically import the Home component and continue just like before.

<Route path="/" exact component={AsyncHome} />

So, basically that’s it. Thanks for reading…

Tags:

Copyright by Kristijan Klepač 2018