Skip to content

Commit df738d8

Browse files
committed
Initial commit.
1 parent 0f9718c commit df738d8

27 files changed

+703
-3322
lines changed

.eslintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"defaultParams": true
1616
},
1717
"rules": {
18-
"react/jsx-filename-extension": 0
18+
"react/jsx-filename-extension": 0,
19+
"react/sort-comp": 0
1920
}
2021
}

.flowconfig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ tools/flow/definitions/
2929

3030
[options]
3131

32+
emoji=true
33+
3234
# This is so that we can import static files in our webpack supported components
3335
# and not have flow throw a hissy fit.
3436
module.name_mapper='^\(.*\)\.\(css\|eot\|gif\|ico\|jpg\|jpeg\|less\|otf\|mp3\|mp4\|ogg\|png\|sass\|scss\|sss\|svg\|swf\|ttf\|webp\|woff\|woff2\)$' -> '<PROJECT_ROOT>/tools/flow/stubs/WebpackAsset.js.flow'
@@ -40,4 +42,4 @@ suppress_comment= \\(.\\|\n\\)*\\$FlowIgnore
4042
suppress_comment= \\(.\\|\n\\)*\\$FlowFixMe
4143

4244
[version]
43-
0.37.4
45+
0.38.0

README.md

Lines changed: 264 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,279 @@
1-
> This is my personal starter kit for npm libraries.
1+
# react-async-component 😴
22

3-
# Your Library Name
3+
Create Components that resolve asynchronously, with support for server side rendering and code splitting.
44

5-
A one liner description of your library.
6-
7-
___BADGES: EDIT THESE TO USE THE URLS FOR EACH SERVICE CONFIGURED FOR YOUR OWN LIBRARY, THEN REMOVE THIS MESSAGE___
8-
9-
[![npm](https://img.shields.io/npm/v/npm-library-starter.svg?style=flat-square)](http://npm.im/npm-library-starter)
10-
[![MIT License](https://img.shields.io/npm/l/npm-library-starter.svg?style=flat-square)](http://opensource.org/licenses/MIT)
11-
[![Travis](https://img.shields.io/travis/ctrlplusb/npm-library-starter.svg?style=flat-square)](https://travis-ci.org/ctrlplusb/npm-library-starter)
12-
[![Codecov](https://img.shields.io/codecov/c/github/ctrlplusb/npm-library-starter.svg?style=flat-square)](https://codecov.io/github/ctrlplusb/npm-library-starter)
5+
[![npm](https://img.shields.io/npm/v/react-async-component.svg?style=flat-square)](http://npm.im/react-async-component)
6+
[![MIT License](https://img.shields.io/npm/l/react-async-component.svg?style=flat-square)](http://opensource.org/licenses/MIT)
7+
[![Travis](https://img.shields.io/travis/ctrlplusb/react-async-component.svg?style=flat-square)](https://travis-ci.org/ctrlplusb/react-async-component)
8+
[![Codecov](https://img.shields.io/codecov/c/github/ctrlplusb/react-async-component.svg?style=flat-square)](https://codecov.io/github/ctrlplusb/react-async-component)
139

1410
## TOCs
1511

1612
- [Introduction](#introduction)
13+
- [Usage](#usage)
14+
- [API](#api)
15+
- [Caveats](#caveats)
1716
- [FAQs](#faqs)
1817

1918
## Introduction
2019

21-
An introduction to your library...
20+
This library is an evolution of [`code-split-component`](). Unlike `code-split-component` this library does not require that you use either Webpack or Babel. Instead it provides you a pure Javascript/React API which has been adapted in a manner to make it generically useful for lazy-loaded Components, with support for modern code splitting APIs (e.g `import()`, `System.import`, `require.ensure`).
2221

23-
## FAQs
22+
## Usage
23+
24+
Firstly, you need to use our helper to allow your application to use asynchronous components in an efficient manner.
25+
26+
```jsx
27+
import { withAsyncComponents } from 'react-async-component'; // 👈
28+
29+
// Declare your application as normal.
30+
const app = <MyApp />;
31+
32+
// Then provide it to our helper, which returns a Promise.
33+
// 👇
34+
withAsyncComponents(app).then((result) => {
35+
// 👆 it resolves a "result" object
36+
const {
37+
// ❗️ We return a new version of your app that supports
38+
// asynchronous components. 💪
39+
appWithAsyncComponents
40+
} = result;
41+
42+
// 🚀 render it!
43+
ReactDOM.render(appWithAsyncComponents, document.getElementById('app'));
44+
});
45+
```
46+
47+
Next up, let's make an asynchronous Component! We provide another helper for this.
48+
49+
```jsx
50+
import { createAsyncComponent } from 'react-async-component'; // 👈
51+
52+
const AsyncProduct = createAsyncComponent({
53+
// Provide a function that will return a Promise that resolves
54+
// as your Component.
55+
resolve: function resolveComponent() {
56+
return new Promise(function (resolve) {
57+
// The Promise the resolves with a simple require of the
58+
// `Product` Component.
59+
resolve(require('./components/Product'));
60+
});
61+
}
62+
});
63+
64+
// You can now use the created Component as though it were a
65+
// "normal" Component, providing it props that will be given
66+
// to the resolved Component.
67+
const x = <Product productId={10} />
68+
```
69+
70+
The above may look a tad bit verbose. If you are a fan of anonymous functions then we could provide a more terse implementation:
71+
72+
```jsx
73+
const AsyncProduct = createAsyncComponent({
74+
resolve: () => new Promise(resolve =>
75+
resolve(require('./components/Product'))
76+
)
77+
});
78+
```
79+
80+
Okay, the above may not look terribly useful at first, but it opens up an easy point to integrating code splitting APIs supported by bundlers such as Webpack. We will provide examples of these as well as details on some other useful configuration options within the [`API`](#api) section.
81+
82+
## API
83+
84+
### `createAsyncComponent(config)`
85+
86+
Our async Component factory. Config goes in, an async Component comes out.
87+
88+
#### Arguments
89+
90+
- `config` : _Object_
91+
The configuration object for the async Component. It has the following properties available:
92+
- `resolve` : _Function => Promise<Component>_
93+
A function that should return a `Promise` that will resolve the Component you wish to be async.
94+
- `defer` : _Boolean_ (Optional, default: false)
95+
Only useful for server side rendering applications. If this is set to true then the async component will only be resolved on the client/browser, not the server. I _highly_ recommend that you consider using this value as much as you can. Try to relieve the load on your server and use server side rendering to provide an application shell for your users. They will still get a perceived performance benefit.
96+
- `Loading` : _Component_ (Optional, default: null)
97+
A Component to be displayed whilst your async Component is being resolved. It will be provided the same props that will be provided to your resovled async Component.
98+
- `es6Aware` : _Boolean_ (Optional, default: true)
99+
If you are using ES2015 modules with default exports (i.e `export default MyComponent`) then you may need your Component resolver to do syntax such as `require('./MyComp').default`. Forgetting the `.default` can cause havoc with hard to debug error messages. To cover your back we will automatically try to resolve a `.default` on the result that is resolved by your Component. If the `.default` exists it will be used, else we will use the original result.
100+
101+
#### Returns
102+
103+
A React Component.
104+
105+
#### Examples
106+
107+
##### Simple
108+
109+
```jsx
110+
const AsyncProduct = createAsyncComponent({
111+
resolve: () => new Promise(resolve =>
112+
resolve(require('./components/Product'))
113+
)
114+
});
115+
116+
<AsyncProduct productId={1} />
117+
```
118+
119+
##### With Loading Component
120+
121+
```jsx
122+
const AsyncProduct = createAsyncComponent({
123+
resolve: () => new Promise(resolve =>
124+
resolve(require('./components/Product'))
125+
),
126+
Loading: ({ productId }) => <div>Loading product {productId}</div>
127+
});
128+
129+
<AsyncProduct productId={1} />
130+
```
131+
132+
##### Webpack 1/2 `require.ensure` Code Splitting
133+
134+
```jsx
135+
const AsyncProduct = createAsyncComponent({
136+
resolve: () => new Promise(resolve =>
137+
require.ensure([], (require) => {
138+
resolve(require('./components/Product'));
139+
});
140+
)
141+
});
24142

25-
___A common question around your library?___
143+
<AsyncProduct productId={1} />
144+
```
26145

27-
The answer to the question.
146+
##### Webpack 2 `import` / `System.import` Code Splitting
28147

29-
___A common question around your library?___
148+
Note: `System.import` is considered deprecated and will be replaced with `import`, but for now they can be used interchangeably (you may need a Babel plugin for the `import` syntax).
149+
150+
```jsx
151+
const AsyncProduct = createAsyncComponent({
152+
resolve: () => System.import('./components/Product')
153+
});
154+
155+
<AsyncProduct productId={1} />
156+
```
157+
158+
#### Defer Loading to the Client/Browser
159+
160+
i.e. The component won't be resolved and rendered in a server side rendering execution.
161+
162+
```jsx
163+
const AsyncProduct = createAsyncComponent({
164+
resolve: () => System.import('./components/Product'),
165+
defer: true
166+
});
167+
```
168+
169+
### `withAsyncComponents(element)`
170+
171+
Decorates your application with the ability to use async Components in an efficient manner. It also manages state storing/rehydrating for server side rendering applications.
172+
173+
### Arguments
174+
175+
- `app` _React.Element_
176+
The react element representing your application.
177+
178+
### Returns
179+
180+
A promise that resolves in a `result` object. The `result` object will have the following properties available:
181+
182+
- `appWithAsyncComponents` _React.Element_
183+
Your application imbued with the ability to use async Components. ❗️Use this when rendering your app.
184+
- `state` _Object_
185+
Only used on the "server" side of server side rendering applications. It represents the state of your async Components (i.e. which ones were rendered) so that the server can feed this information back to the client/browser.
186+
- `STATE_IDENTIFIER` _String_
187+
Only used on the "server" side of server side rendering applications. The identifier of the property you should bind the `state` object to on the `window` object.
188+
189+
### Examples
190+
191+
#### Usage
192+
193+
```jsx
194+
import React from 'react';
195+
import { render } from 'react-dom';
196+
import { withAsyncComponents } from 'react-async-component'; // 👈
197+
import MyApp from './shared/components/MyApp';
198+
199+
const app = <MyApp />
200+
201+
// 👇 run helper on your app.
202+
withAsyncComponents(app)
203+
// 👇 and you get back a result object.
204+
.then((result) => {
205+
const {
206+
// ❗️ The result includes a decorated version of your app
207+
// that will allow your application to use async components
208+
// in an efficient manner.
209+
appWithAsyncComponents
210+
} = result;
211+
212+
// Now you can render the app.
213+
render(appWithAsyncComponents, document.getElementById('app'));
214+
});
215+
```
216+
217+
#### Server Side Rendering Usage
218+
219+
When using this helper on the "server" side of your server side rendering applications you should do the following.
220+
221+
> Note: on the "client" side of a server side rendering application you can use the helper in the "nomral" fashion as detailed in the previous example.
222+
223+
```js
224+
import React from 'react';
225+
import { withAsyncComponents } from 'react-async-component'; // 👈
226+
import { renderToString } from 'react-dom/server';
227+
import serialize from 'serialize-javascript';
228+
import MyApp from './shared/components/MyApp';
229+
230+
export default function expressMiddleware(req, res, next) {
231+
const app = <MyApp />;
232+
233+
// 👇 run helper on your app.
234+
withAsyncComponents(app)
235+
// 👇 and you get back a result object.
236+
.then((result) => {
237+
const {
238+
// ❗️ The result includes a decorated version of your app
239+
// that will have the async components initialised for
240+
// the renderToString call.
241+
appWithAsyncComponents,
242+
// This state object represents the async components that
243+
// were rendered by the server. We will need to send
244+
// this back to the client, attaching it to the window
245+
// object so that the client can rehydrate the application
246+
// to the expected state and avoid React checksum issues.
247+
state,
248+
// This is the identifier you should use when attaching
249+
// the state to the "window" object.
250+
STATE_IDENTIFIER
251+
} = result;
252+
253+
const appString = renderToString(appWithAsyncComponents);
254+
const html = `
255+
<html>
256+
<head>
257+
<title>Example</title>
258+
</head>
259+
<body>
260+
<div id="app">${appString}</div>
261+
<script type="text/javascript">
262+
window.${STATE_IDENTIFIER} = ${serialize(state)}
263+
</script>
264+
</body>
265+
</html>`;
266+
res.send(html);
267+
});
268+
}
269+
```
270+
271+
## Caveats
272+
273+
At the moment there is one known caveat in using this library: it doesn't support React Hot Loader (RHL). You can still use Webpack's standard Hot Module Replacement, however, RHL does not respond nicely to the architecture of `react-async-component`.
274+
275+
TODO: I'll post up some details why and perhaps we could work to find a solution.
276+
277+
## FAQs
30278

31-
The answer to the question.
279+
> Let me know if you have any...

examples/.eslintrc

Lines changed: 0 additions & 5 deletions
This file was deleted.

examples/web/.babelrc

Lines changed: 0 additions & 7 deletions
This file was deleted.

examples/web/package.json

Lines changed: 0 additions & 29 deletions
This file was deleted.

examples/web/server.js

Lines changed: 0 additions & 25 deletions
This file was deleted.

examples/web/src/index.js

Lines changed: 0 additions & 8 deletions
This file was deleted.

0 commit comments

Comments
 (0)