Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is lazy in React?

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 dynamic import(). This must return a Promise 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?

like image 806
Hemadri Dasari Avatar asked Nov 06 '18 15:11

Hemadri Dasari


6 Answers

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, lazylets 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.

like image 193
Mrchief Avatar answered Oct 11 '22 05:10

Mrchief


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
Edit so.answer.53174915

When you inspect the deployed site on Netlify,
you can see from the site that Bye is not loaded.

enter image description here


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.

like image 44
dance2die Avatar answered Oct 11 '22 04:10

dance2die


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:

  • Your initial start-up is faster (assuming your component isn't part of your initial display)
  • If the user never does whatever it is that shows your component, the other component's script never needs to be loaded (so that's saved an HTTP request and the memory loading that module consumes)

Of course, there are disadvantages:

  • When your component needs to be shown, it will be delayed by demand-loading the other component's script (HTTP request, top-level module evaluation)
  • Complexity if you're bundling, now you have to have some scripts in the bundle and other scripts not in the bundle

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.

like image 30
T.J. Crowder Avatar answered Oct 11 '22 05:10

T.J. Crowder


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.

like image 38
guru107 Avatar answered Oct 11 '22 06:10

guru107


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.

like image 1
Arnab Avatar answered Oct 11 '22 05:10

Arnab


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

  1. js isn't a very helpful filename. Fortunately with 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

like image 1
Oussama Filani Avatar answered Oct 11 '22 05:10

Oussama Filani