Skip to content

Commit cec03b4

Browse files
committed
Huuuuuge update.
- React Hot Loader interoperability. 🎉 - The provider is no longer required for a browser-only context. - `ssrMode` config renamed to `serverMode`. - Errors are no longer server rendered. - Code simplification parse.
1 parent af77fa3 commit cec03b4

10 files changed

+462
-445
lines changed

README.md

+18-30
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ Resolve components asynchronously, with support for code splitting and advanced
1212
```jsx
1313
const Product = asyncComponent({
1414
resolve: () => System.import('./AsyncProduct'),
15-
LoadingComponent: ({ productId }) => <div>Loading {productId}</div>,
16-
ErrorComponent: ({ error }) => <div>{error.message}</div>
15+
LoadingComponent: ({ productId }) => <div>Loading {productId}</div>, // Optional
16+
ErrorComponent: ({ error }) => <div>{error.message}</div> // Optional
1717
});
1818

1919
<Product productId={1} /> // 🚀
@@ -45,25 +45,7 @@ This library does not require that you use either Webpack or Babel. Instead it
4545

4646
## Usage
4747

48-
Firstly, you need to wrap the rendering of your application with the `AsyncComponentProvider`:
49-
50-
```jsx
51-
import React from 'react';
52-
import ReactDOM from 'react-dom';
53-
import { AsyncComponentProvider } from 'react-async-component'; // 👈
54-
import MyApp from './components/MyApp';
55-
56-
const app = (
57-
// 👇
58-
<AsyncComponentProvider>
59-
<MyApp />
60-
</AsyncComponentProvider>
61-
);
62-
63-
ReactDOM.render(app, document.getElementById('app'));
64-
```
65-
66-
Now, let's create an asynchronous `Product` component. I recommend that you use the following folder/file for your asynchronous components:
48+
When creating your asynchronous components I recommend that you use the following folder/file structure:
6749

6850
```
6951
|- components
@@ -77,7 +59,7 @@ __`./components/Product/index.js`__:
7759
```js
7860
import { asyncComponent } from 'react-async-component';
7961

80-
// Create async component 👇
62+
// Create an async component👇
8163
export default asyncComponent({
8264
resolve: () => System.import('./AsyncProduct')
8365
// That resolves to 👆
@@ -94,7 +76,7 @@ export default function Product({ productId }) {
9476
}
9577
```
9678

97-
Now, you can simply import `Product` anywhere in your application and not have to worry about having to call `createAsyncComponent` again.
79+
Now, you can simply import `Product` anywhere in your application and use it exactly as you would any other component.
9880

9981
For example:
10082

@@ -131,9 +113,9 @@ The asynchronous component factory. Config goes in, an asynchronous component co
131113
- `ErrorComponent` (_Component_, Optional, default: `null`) : A Component that will be displayed if any error occurred whilst trying to resolve your component. All props will be passed to it as well as an `error` prop which is an object with a `message` and `stack` property.
132114
- `name` (_String_, Optional, default: `'AsyncComponent'`) : Use this if you would like to name the created async Component, which helps when firing up the React Dev Tools for example.
133115
- `autoResolveES2015Default` (_Boolean_, Optional, default: `true`) : Especially useful if you are resolving ES2015 modules. The resolved module will be checked to see if it has a `.default` and if so then the value attached to `.default` will be used. So easy to forget to do that. 😀
134-
- `ssrMode` (_Boolean_, Optional, default: `'render'`) : Only applies for server side rendering applications. Please see the documentation on server side rendering. The following values are allowed.
116+
- `serverMode` (_Boolean_, Optional, default: `'render'`) : Only applies for server side rendering applications. Please see the documentation on server side rendering. The following values are allowed.
135117
- __`'render'`__ - Your asynchronous component will be resolved and rendered on the server. It's children will
136-
be checked to see if there are any nested asynchronous component instances, which will then be processed based on the `ssrMode` value that was associated with them.
118+
be checked to see if there are any nested asynchronous component instances, which will then be processed based on the `serverMode` value that was associated with them.
137119
- __`'defer'`__ - Your asynchronous component will _not_ be rendered on the server, instead deferring rendering to the client/browser.
138120
- __`'boundary'`__ - Your asynchronous component will be resolved and rendered on the server. However, if it has a nested asynchronous component instance within it's children that component will be ignored and treated as being deferred for rendering in the client/browser instead.
139121
We highly recommend that you consider using `defer` as much as you can.
@@ -186,12 +168,12 @@ export default asyncComponent({
186168

187169
### `<AsyncComponentProvider />`
188170

189-
Wraps your application allowing for efficient and effective use of asynchronous components.
171+
Currently only useful when building server side rendering applications. Wraps your application allowing for efficient and effective use of asynchronous components.
190172

191173
#### Props
192174

193-
- `asyncContext` (_Object_, Optional) : You can optionally provide an asynchronous context (created using `createAsyncContext`). If you don't provide one then an instance will be created automatically internally. It can be useful to create and provide your own instance so that you can gain hooks into the context for advanced use cases such as server side rendering state rehydration or hot module replacement interoperability.
194-
- `rehydrateState` (_Object_, Optional) : Only useful in a server side rendering application (see the docs). This allows you to provide the state returned by the server to be used to rehydrate the client appropriately, ensuring that it's rendered checksum will match that of the content rendered by the server.
175+
- `asyncContext` (_Object_) : Used so that you can gain hooks into the context for server side rendering render tracking and rehydration. See the `createAsyncContext` helper for creating an instance.
176+
- `rehydrateState` (_Object_, Optional) : Used on the "browser-side" of a server side rendering application (see the docs). This allows you to provide the state returned by the server to be used to rehydrate the client appropriately.
195177

196178
### `createAsyncContext()`
197179

@@ -201,7 +183,13 @@ Creates an asynchronous context that can be used by the `<AsyncComponentProvider
201183

202184
## Server Side Rendering
203185

204-
This library has been designed for generic interoperability with [`react-async-bootstrapper`](https://github.com/ctrlplusb/react-async-bootstrapper). The `react-async-bootstrapper` utility allows us to do a "pre-render parse" of a React Element tree and execute any "bootstrapping" functions that are attached to a components within the tree. In our case the "bootstrapping" process involves the resolution of asynchronous components so that they can be rendered "synchronously" by the server. We use this 3rd party library as it allows interoperability with other libraries which also require a "bootstrapping" process (e.g. data preloading as supported by [`react-jobs`](https://github.com/ctrlplusb/react-jobs)).
186+
> NOTE: This section only really applies if you would like to have control over the behaviour of how your asyncComponent instances are rendered on the server. If you don't mind your asyncComponents always being resolved on the client only then you need not do any of the below. In my opinion there is great value in just server rendering your app shell and having everything else resolve on the client, however, you may have very strict SEO needs - in which case, we have your back.
187+
188+
This library has been designed for interoperability with [`react-async-bootstrapper`](https://github.com/ctrlplusb/react-async-bootstrapper).
189+
190+
`react-async-bootstrapper` allows us to do a "pre-render parse" of our React Element tree and execute an `asyncBootstrap` function that are attached to a components within the tree. In our case the "bootstrapping" process involves the resolution of asynchronous components so that they can be rendered "synchronously" by the server. We use this 3rd party library as it allows interoperability with other libraries which also require a "bootstrapping" process (e.g. data preloading as supported by [`react-jobs`](https://github.com/ctrlplusb/react-jobs)).
191+
192+
> NOTE: I have not updated `react-jobs` to make use of `react-async-bootstrapper` as of yet.
205193
206194
Firstly, install `react-async-bootstrapper`:
207195

@@ -303,7 +291,7 @@ Although this operation isn't as expensive as an actual render as we don't gener
303291

304292
### SSR Performance Optimisation
305293

306-
As discussed in the ["SSR AsyncComponent Resolution Process"](#ssr-asyncComponent-resolution-process) section above it is possible to have an inefficient implementation of your `asyncComponent` instances. Therefore we introduced a new configuration object property for the `asyncComponent` factory, called `ssrMode`, which provides you with a mechanism to optimise the configuration of your async Component instances. Please see the API documentation for more information.
294+
As discussed in the ["SSR AsyncComponent Resolution Process"](#ssr-asyncComponent-resolution-process) section above it is possible to have an inefficient implementation of your `asyncComponent` instances. Therefore we introduced a new configuration object property for the `asyncComponent` factory, called `serverMode`, which provides you with a mechanism to optimise the configuration of your async Component instances. Please see the API documentation for more information.
307295

308296
Understand your own applications needs and use the options appropriately . I personally recommend using mostly "defer" and a bit of "boundary". Try to see code splitting as allowing you to server side render an application shell to give the user perceived performance. Of course there will be requirements otherwise (SEO), but try to isolate these components and use a "boundary" as soon as you feel you can.
309297

package.json

+11-11
Original file line numberDiff line numberDiff line change
@@ -61,44 +61,44 @@
6161
"app-root-dir": "1.0.2",
6262
"babel-cli": "6.24.0",
6363
"babel-core": "6.24.0",
64-
"babel-eslint": "7.2.0",
64+
"babel-eslint": "7.2.1",
6565
"babel-jest": "19.0.0",
6666
"babel-loader": "6.4.1",
6767
"babel-polyfill": "6.23.0",
68-
"babel-preset-env": "1.2.2",
68+
"babel-preset-env": "1.3.2",
6969
"babel-preset-latest": "6.24.0",
7070
"babel-preset-react": "6.23.0",
7171
"babel-preset-stage-3": "6.22.0",
7272
"babel-register": "6.24.0",
7373
"codecov": "2.1.0",
74-
"cross-env": "3.2.4",
75-
"enzyme": "2.7.1",
74+
"cross-env": "4.0.0",
75+
"enzyme": "2.8.0",
7676
"enzyme-to-json": "1.5.0",
77-
"eslint": "3.18.0",
77+
"eslint": "3.19.0",
7878
"eslint-config-airbnb": "14.1.0",
7979
"eslint-plugin-import": "2.2.0",
8080
"eslint-plugin-jsx-a11y": "4.0.0",
8181
"eslint-plugin-react": "6.10.3",
8282
"gzip-size": "3.0.0",
83-
"husky": "0.13.2",
83+
"husky": "0.13.3",
8484
"in-publish": "2.0.0",
8585
"jest": "19.0.2",
8686
"lint-staged": "3.4.0",
8787
"memory-fs": "0.4.1",
8888
"prettier": "0.22.0",
89-
"prettier-eslint": "4.3.2",
90-
"prettier-eslint-cli": "^3.1.2",
89+
"prettier-eslint": "4.4.0",
90+
"prettier-eslint-cli": "3.2.0",
9191
"pretty-bytes": "4.0.2",
9292
"ramda": "0.23.0",
9393
"react": "15.4.2",
9494
"react-addons-test-utils": "15.4.2",
95-
"react-async-bootstrapper": "0.0.5",
95+
"react-async-bootstrapper": "^1.0.0",
9696
"react-dom": "15.4.2",
9797
"readline-sync": "1.4.7",
9898
"rimraf": "2.6.1",
9999
"sinon": "2.1.0",
100-
"webpack": "2.2.1",
100+
"webpack": "2.3.3",
101101
"webpack-dev-middleware": "1.10.1",
102-
"webpack-hot-middleware": "2.17.1"
102+
"webpack-hot-middleware": "2.18.0"
103103
}
104104
}

src/AsyncComponentProvider.js

+7-23
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,11 @@ class AsyncComponentProvider extends React.Component {
1212
return {
1313
asyncComponents: {
1414
getNextId: this.asyncContext.getNextId,
15-
registerComponent: this.asyncContext.registerComponent,
16-
getComponent: this.asyncContext.getComponent,
17-
registerError: this.asyncContext.registerError,
18-
getError: this.asyncContext.getError,
19-
getRehydrate: (id) => {
20-
const error = this.rehydrateState.errors[id]
15+
resolved: this.asyncContext.resolved,
16+
shouldRehydrate: (id) => {
2117
const resolved = this.rehydrateState.resolved[id]
22-
delete this.rehydrateState.errors[id]
2318
delete this.rehydrateState.resolved[id]
24-
return {
25-
// eslint-disable-next-line no-nested-ternary
26-
type: error ? 'error' : resolved ? 'resolved' : 'unresolved',
27-
error,
28-
}
19+
return resolved
2920
},
3021
},
3122
}
@@ -40,33 +31,26 @@ AsyncComponentProvider.propTypes = {
4031
children: React.PropTypes.node.isRequired,
4132
asyncContext: React.PropTypes.shape({
4233
getNextId: React.PropTypes.func.isRequired,
43-
registerComponent: React.PropTypes.func.isRequired,
44-
getComponent: React.PropTypes.func.isRequired,
45-
registerError: React.PropTypes.func.isRequired,
46-
getError: React.PropTypes.func.isRequired,
34+
resolved: React.PropTypes.func.isRequired,
35+
getState: React.PropTypes.func.isRequired,
4736
}),
4837
rehydrateState: React.PropTypes.shape({
4938
resolved: React.PropTypes.object,
50-
errors: React.PropTypes.object,
5139
}),
5240
}
5341

5442
AsyncComponentProvider.defaultProps = {
5543
asyncContext: undefined,
5644
rehydrateState: {
5745
resolved: {},
58-
errors: {},
5946
},
6047
}
6148

6249
AsyncComponentProvider.childContextTypes = {
6350
asyncComponents: React.PropTypes.shape({
6451
getNextId: React.PropTypes.func.isRequired,
65-
registerComponent: React.PropTypes.func.isRequired,
66-
getComponent: React.PropTypes.func.isRequired,
67-
registerError: React.PropTypes.func.isRequired,
68-
getError: React.PropTypes.func.isRequired,
69-
getRehydrate: React.PropTypes.func.isRequired,
52+
resolved: React.PropTypes.func.isRequired,
53+
shouldRehydrate: React.PropTypes.func.isRequired,
7054
}).isRequired,
7155
}
7256

0 commit comments

Comments
 (0)