Skip to content

Commit a347981

Browse files
committed
Lazy Loading / Code Splitting
- Lazy Loading Components Asynchronously with `React.lazy()` method & `<React.Suspense fallback={<div>Loading...</div>} />` Component
1 parent 292015e commit a347981

3 files changed

Lines changed: 42 additions & 107 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,5 +152,6 @@ Required Dependency: **`npm i --save axios`** | **[`axios` DOCS](https://github.
152152
4. Using `this.props.history.push()/replace()` to Redirect: [Commit Details](https://github.com/Ch-sriram/react/commit/c34bd8846f6916159ef4a6ca58849dab760ae060)
153153
5. Working with Navigation Guards: [Commit Details](https://github.com/Ch-sriram/react/commit/69cffe5d7ceceae4c7e3d1db90287978c1ea0740)
154154
6. Handling the **404 Not Found** Case using `path`less `<Route />`: [Commit Details](https://github.com/Ch-sriram/react/commit/643d0c2cc9a19a00207d34ce586c16609e1dae04)
155-
7. **Lazy Loading / Code Splitting** 😴 &mdash; 💥Important💥
155+
7. **Lazy Loading / Code Splitting** 😴 &mdash; 💥**Important**💥
156156
1. Loading Routes Lazily Using Asynchronous Dynamic `import()` method inside our own custom-made HOC: [Commit Details](https://github.com/Ch-sriram/react/commit/20387d909c7a69c80736eec746c2d07314314bdc)
157+
2. Lazy Loading Components Asynchronously with `React.lazy()` method & `<React.Suspense fallback={<div>Loading...</div>} />` Component: [Commit Details]()
Lines changed: 39 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,87 +1,40 @@
11
// LIBRARY IMPORTS
2-
import React, { Component } from "react";
2+
import React, { Component, Suspense } from "react";
33
import { Route, NavLink, Switch, Redirect } from 'react-router-dom';
44

55
// STYLING IMPORTS
66
import "./Blog.css";
77

8-
/**
9-
* One concept, which is very important, and easy to implement,
10-
* is Code Splitting (aka Lazy Loading), where we load routes/
11-
* components that we don't require all the time, to be loaded
12-
* when they're required (or when they're requested for by the
13-
* user).
14-
*
15-
* Without Lazy Loading, what we generally do is, we try to
16-
* get the entire `bundle.js` file whenever we request for the
17-
* webapp. For smaller webapps, loading the entire app makes
18-
* sense. But if an application is extremely big, in that
19-
* scenario, we might want to lazy load certain components
20-
* when they're required/requested by the user. Such components
21-
* in the webapp generally exits for different routes, or
22-
* sometimes, at the component level itself. The solution is to
23-
* lazy load such components/routes entirely, using the new ES6
24-
* async import() method.
25-
*
26-
* We generally create a HOC inside which we basically pass an
27-
* component, which would be loaded lazily (asynchronously),
28-
* which is the HOC created as a LazyLoader inside the `hoc`
29-
* directory in our webapp.
30-
*
31-
* For this lazy loading example, we will set the state for
32-
* `auth` to true, so that we have access to the "/new-post"
33-
* route. This "/new-post" route can be lazy loaded, only when
34-
* the user wants to post a new post onto the database.
35-
* Therefore, loading the component related to the "/new-post"
36-
* route is unnecessary, and so, we need not load the component
37-
* related to "/new-post" route upfront, instead, we can always
38-
* lazy load the respective component and its child components
39-
* when the user wants/needs it.
40-
*
41-
* This technique of downloading/acquiring a resource
42-
* (component in this case) only when needed by the end-user,
43-
* is known as Code Splitting (or) Lazy Loading.
44-
*
45-
* NOTE: Code Splitting is heavily dependent on the WEBPACK
46-
* configuration. The Webpack config generated from the
47-
* `npx create-react-app` command, has a webpack configuration
48-
* which supports the implementation of code splitting as shown
49-
* in here.
50-
*/
51-
528
// CUSTOM COMPONENTS
53-
/**
54-
* Direct imports inform Webpack (The Build Tool used BTS) that
55-
* this particular component that's being imported, is a
56-
* dependency which is to be included directly inside
57-
* `bundle.js` file (which is the final file to deployed).
58-
*
59-
* For Lazy Loading, we don't want to import the component
60-
* (or resource) directly, as shown above for React and
61-
* Component (or for Posts as shown below), but we want to
62-
* load it only when needed.
63-
*
64-
* For that, we need to prepare Webpack to dynamically bundle
65-
* some extra resource/component for this potentially loaded
66-
* code.
67-
*
68-
* Therefore, to import the <NewPost /> component dynamically
69-
* (for lazy loading on "/new-post/" route), we will import the
70-
* HOC which lazy loads the required component passed to the
71-
* dynamic import() method, and returns the respective
72-
* component related to the specified module which is mentioned
73-
* as a parameter to the dynamic import() method.
74-
*/
759
import Posts from '../../containers/Blog/Posts/Posts'; // direct import
7610
// import NewPost from './NewPost/NewPost';
77-
import asyncLazyLoadComponent from '../../hoc/LazyLoader/asyncLazyComponent';
11+
// import asyncLazyLoadComponent from '../../hoc/LazyLoader/asyncLazyComponent';
12+
13+
/**
14+
* Instead of using our own custom-made HOC, from React v16.6,
15+
* we can use React.lazy() method and replace what our
16+
* asyncLazyLoadComponent() method did. The lazy() method also
17+
* takes in an anonymous callback function which returns a
18+
* call to the dynamic import() method, which returns a Promise
19+
*/
7820

7921
// this following line is a dynamic import using the import()
80-
const AsyncNewPost = asyncLazyLoadComponent(() => import('./NewPost/NewPost'));
22+
// const AsyncNewPost = asyncLazyLoadComponent(() => import('./NewPost/NewPost'));
23+
24+
// Instead of the using our custom HOC for lazy loading the
25+
// <NewPost /> component, we can now use React.lazy() as shown
26+
// below.
27+
const AsyncNewPost = React.lazy(() => import('./NewPost/NewPost')); // only supports default exports.
28+
29+
/**
30+
* Now, we will render the <NewPost /> which is now our
31+
* <AsyncNewPost /> component inside a <React.Suspense />
32+
* Component as shown in the `render` prop of the <Route />
33+
* component defined for the "/new-post" `path` below (line #72)
34+
*/
8135

8236
class Blog extends Component {
8337
state = { auth: true };
84-
8538
render() {
8639
return (
8740
<div className="Blog">
@@ -115,16 +68,16 @@ class Blog extends Component {
11568
</ul>
11669
</nav>
11770
</header>
118-
{
119-
/**
120-
* Now, we can use the <AsyncNewPost /> component
121-
* that we will get dynamically, only when the
122-
* component related to the "/new-post" route is
123-
* routed/clicked by the user.
124-
*/
125-
}
12671
<Switch>
127-
{this.state.auth ? <Route path="/new-post" component={AsyncNewPost} /> : null}
72+
{this.state.auth ?
73+
<Route
74+
path="/new-post"
75+
render={() =>
76+
<Suspense fallback={<div style={{ textAlign: 'center' }}>Loading...</div>}>
77+
<AsyncNewPost />
78+
</Suspense>
79+
}
80+
/> : null}
12881
<Route path="/posts" component={Posts} />
12982
<Route render={() => <h1 style={{textAlign: 'center'}}>404 Not Found</h1>} />
13083
{/*<Redirect from="/" to="/posts" />*/}
@@ -138,14 +91,12 @@ class Blog extends Component {
13891
export default Blog;
13992

14093
/**
141-
* Now, whenever we click on the "New Post" link (or get
142-
* routed to "/new-post" route), we might not see any changes
143-
* upfront, but if we open the `Network` tab of the Dev Tools
144-
* of our browser, and then see what is happening when we go
145-
* to the "/new-post" route for the first, we can see that we
146-
* are actually downloading the required JS file as a small
147-
* chunk from the server (thanks to HTTP v2) asynchronously.
94+
* VERY IMPORTANT (README) on React.lazy() & <React.Suspense />
95+
* ------------------------------------------------------------
14896
*
149-
* Whereas, before lazy loading the <NewPost /> component, we
150-
* were getting it inside the bundle.js file at once.
97+
* We can use lazy loading for any component using
98+
* React.lazy() method along with the <React.Suspense />
99+
* wrapper. We can use it even for loading components which
100+
* aren't rendered from the <Route /> component defined
101+
* inside the <BrowserRouter /> component.
151102
*/

http-ajax-routing/src/hoc/LazyLoader/asyncLazyComponent.js

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,13 @@
1-
/**
2-
* This component is a HOC which wraps a component generated
3-
* asynchronously and loaded only when it is needed.
4-
*/
5-
61
import React, { Component } from 'react';
72

8-
// Returns an Async Component (which is a Promise)
3+
// Returns a Component (takes in a Callback function which returns a Promise)
94
const asyncLazyComponent = importAsyncComponentPromise => {
105
return class extends Component {
116
// We always maintain a state which stores a component
127
state = {
138
component: null, // initial value of component is null
149
}
1510

16-
// importAsyncComponentPromise is a callback function which
17-
// returns a Promise. The Promise on Resolve state (i.e.,
18-
// inside the .then(response)) returns a component, which
19-
// is our component that's to be lazy-loaded onto the view.
20-
2111
// That component (say `cmp`) is set in this state's
2212
// component using the setState method, where the cmp has
2313
// a default field, which has the actual component.
@@ -31,13 +21,6 @@ const asyncLazyComponent = importAsyncComponentPromise => {
3121
.catch(err => err);
3222
}
3323

34-
// Now, at some point of time, we would've loaded the
35-
// actual component from the dynamic import(), and it will
36-
// be store in our state inside the `component` field.
37-
38-
// As this is an HOC, we return component after we render
39-
// it, to the dynamic import() being called wherever this
40-
// asyncLazyComponent HOC is called.
4124
render() {
4225
const Component = this.state.component;
4326
return Component ? <Component {...this.props} /> : null;

0 commit comments

Comments
 (0)