Skip to content

Commit 9dbc835

Browse files
committed
Fixes issue where errors would be swallowed. They now get reported always on node run, and will get reported on browser if no ErrorComponent is specified.
Updates dependencies. Updates documentation.
1 parent 89caadd commit 9dbc835

10 files changed

+211
-200
lines changed

.babelrc

+9
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,29 @@
44
"stage-3",
55
"react"
66
],
7+
"plugins": [
8+
"transform-class-properties"
9+
],
710
"env": {
811
"commonjs": {
912
"presets": [
1013
"latest",
1114
"stage-3",
1215
"react"
1316
],
17+
"plugins": [
18+
"transform-class-properties"
19+
]
1420
},
1521
"umd": {
1622
"presets": [
1723
["latest", { "es2015": { "modules": false } }],
1824
"stage-3",
1925
"react"
2026
],
27+
"plugins": [
28+
"transform-class-properties"
29+
]
2130
}
2231
}
2332
}

README.md

+5-12
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,11 @@ The asynchronous component factory. Config goes in, an asynchronous component co
100100
- `config` (_Object_) : The configuration object for the async Component. It has the following properties available:
101101
- `resolve` (_() => Promise<Component>_) : A function that should return a `Promise` that will resolve the Component you wish to be async.
102102
- `LoadingComponent` (_Component_, Optional, default: `null`) : A Component that will be displayed until your async Component is resolved. All props will be passed to it.
103-
- `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.
103+
- `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 containing the `Error`.
104104
- `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.
105105
- `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. 😀
106-
- `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.
107-
- __`'render'`__ - Your asynchronous component will be resolved and rendered on the server. It's children will
106+
- `serverMode` (_Boolean_, Optional, default: `'resolve'`) : Only applies for server side rendering applications. Please see the documentation on server side rendering. The following values are allowed.
107+
- __`'resolve'`__ - Your asynchronous component will be resolved and rendered on the server. It's children will
108108
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.
109109
- __`'defer'`__ - Your asynchronous component will _not_ be rendered on the server, instead deferring rendering to the client/browser.
110110
- __`'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 (it's serverMode will be ignored).
@@ -210,7 +210,7 @@ export default function expressMiddleware(req, res, next) {
210210
// We can now render our app 👇
211211
const appString = renderToString(app)
212212

213-
// 👇 Get the async component state.
213+
// Get the async component state. 👇
214214
const asyncState = asyncContext.getState()
215215

216216
const html = `
@@ -244,17 +244,10 @@ import MyApp from './components/MyApp'
244244
// 👇 Get any "rehydrate" state sent back by the server
245245
const rehydrateState = window.ASYNC_COMPONENTS_STATE
246246

247-
// Create an async context so that state can be tracked
248-
// 👇 across the bootstrapping and rendering process.
249-
const asyncContext = createAsyncContext()
250-
251247
// Ensure you wrap your application with the provider,
252248
// 👇 and pass in the rehydrateState.
253249
const app = (
254-
<AsyncComponentProvider
255-
rehydrateState={rehydrateState}
256-
asyncContext={asyncContext}
257-
>
250+
<AsyncComponentProvider rehydrateState={rehydrateState}>
258251
<MyApp />
259252
</AsyncComponentProvider>
260253
)

commonjs/AsyncComponentProvider.js

-3
Original file line numberDiff line numberDiff line change
@@ -75,20 +75,17 @@ AsyncComponentProvider.propTypes = {
7575
resolved: _react2.default.PropTypes.object
7676
})
7777
};
78-
7978
AsyncComponentProvider.defaultProps = {
8079
asyncContext: undefined,
8180
rehydrateState: {
8281
resolved: {}
8382
}
8483
};
85-
8684
AsyncComponentProvider.childContextTypes = {
8785
asyncComponents: _react2.default.PropTypes.shape({
8886
getNextId: _react2.default.PropTypes.func.isRequired,
8987
resolved: _react2.default.PropTypes.func.isRequired,
9088
shouldRehydrate: _react2.default.PropTypes.func.isRequired
9189
}).isRequired
9290
};
93-
9491
exports.default = AsyncComponentProvider;

commonjs/asyncComponent.js

+50-50
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ Object.defineProperty(exports, "__esModule", {
44
value: true
55
});
66

7+
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; };
8+
79
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
810

911
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
@@ -20,17 +22,17 @@ function _possibleConstructorReturn(self, call) { if (!self) { throw new Referen
2022

2123
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
2224

23-
var validSSRModes = ['render', 'defer', 'boundary'];
25+
var validSSRModes = ['resolve', 'defer', 'boundary'];
2426

25-
function asyncComponent(args) {
26-
var name = args.name,
27-
resolve = args.resolve,
28-
_args$autoResolveES = args.autoResolveES2015Default,
29-
autoResolveES2015Default = _args$autoResolveES === undefined ? true : _args$autoResolveES,
30-
_args$serverMode = args.serverMode,
31-
serverMode = _args$serverMode === undefined ? 'render' : _args$serverMode,
32-
LoadingComponent = args.LoadingComponent,
33-
ErrorComponent = args.ErrorComponent;
27+
function asyncComponent(config) {
28+
var name = config.name,
29+
resolve = config.resolve,
30+
_config$autoResolveES = config.autoResolveES2015Default,
31+
autoResolveES2015Default = _config$autoResolveES === undefined ? true : _config$autoResolveES,
32+
_config$serverMode = config.serverMode,
33+
serverMode = _config$serverMode === undefined ? 'resolve' : _config$serverMode,
34+
LoadingComponent = config.LoadingComponent,
35+
ErrorComponent = config.ErrorComponent;
3436

3537

3638
if (validSSRModes.indexOf(serverMode) === -1) {
@@ -91,7 +93,35 @@ function asyncComponent(args) {
9193
return _this;
9294
}
9395

96+
// @see react-async-bootstrapper
97+
98+
9499
_createClass(AsyncComponent, [{
100+
key: 'asyncBootstrap',
101+
value: function asyncBootstrap() {
102+
var _this2 = this;
103+
104+
var _context = this.context,
105+
asyncComponents = _context.asyncComponents,
106+
asyncComponentsAncestor = _context.asyncComponentsAncestor;
107+
var shouldRehydrate = asyncComponents.shouldRehydrate;
108+
109+
110+
var doResolve = function doResolve() {
111+
return _this2.resolveModule().then(function (module) {
112+
return module !== undefined;
113+
});
114+
};
115+
116+
if (env === 'browser') {
117+
return shouldRehydrate(sharedState.id) ? doResolve() : false;
118+
}
119+
120+
// node
121+
var isChildOfBoundary = asyncComponentsAncestor && asyncComponentsAncestor.isBoundary;
122+
return serverMode === 'defer' || isChildOfBoundary ? false : doResolve();
123+
}
124+
}, {
95125
key: 'getChildContext',
96126
value: function getChildContext() {
97127
if (!this.context.asyncComponents) {
@@ -119,35 +149,6 @@ function asyncComponent(args) {
119149
this.resolveModule();
120150
}
121151
}
122-
123-
// @see react-async-bootstrapper
124-
125-
}, {
126-
key: 'asyncBootstrap',
127-
value: function asyncBootstrap() {
128-
var _this2 = this;
129-
130-
var _context = this.context,
131-
asyncComponents = _context.asyncComponents,
132-
asyncComponentsAncestor = _context.asyncComponentsAncestor;
133-
var shouldRehydrate = asyncComponents.shouldRehydrate;
134-
135-
136-
var doResolve = function doResolve() {
137-
return _this2.resolveModule().then(function (module) {
138-
return module !== undefined;
139-
});
140-
};
141-
142-
if (typeof window !== 'undefined') {
143-
// BROWSER BASED LOGIC
144-
return shouldRehydrate(sharedState.id) ? doResolve() : false;
145-
}
146-
147-
// SERVER BASED LOGIC
148-
var isChildOfBoundary = asyncComponentsAncestor && asyncComponentsAncestor.isBoundary;
149-
return serverMode === 'defer' || isChildOfBoundary ? false : doResolve();
150-
}
151152
}, {
152153
key: 'resolveModule',
153154
value: function resolveModule() {
@@ -172,13 +173,13 @@ function asyncComponent(args) {
172173
if (_this3.unmounted) {
173174
return undefined;
174175
}
175-
if (env === 'node' || !ErrorComponent) {
176+
if (env === 'node' || env === 'browser' && !ErrorComponent) {
176177
// We will at least log the error so that user isn't completely
177178
// unaware of an error occurring.
178179
// eslint-disable-next-line no-console
179-
// console.warn('Failed to resolve asyncComponent')
180+
console.warn('Failed to resolve asyncComponent');
180181
// eslint-disable-next-line no-console
181-
// console.warn(error)
182+
console.warn(error);
182183
}
183184
sharedState.error = error;
184185
_this3.registerErrorState(error);
@@ -221,7 +222,7 @@ function asyncComponent(args) {
221222
}
222223

223224
if (error) {
224-
return ErrorComponent ? _react2.default.createElement(ErrorComponent, { error: error }) : null;
225+
return ErrorComponent ? _react2.default.createElement(ErrorComponent, _extends({}, this.props, { error: error })) : null;
225226
}
226227

227228
var Component = es6Resolve(module);
@@ -233,12 +234,7 @@ function asyncComponent(args) {
233234
return AsyncComponent;
234235
}(_react2.default.Component);
235236

236-
AsyncComponent.childContextTypes = {
237-
asyncComponentsAncestor: _react2.default.PropTypes.shape({
238-
isBoundary: _react2.default.PropTypes.bool
239-
})
240-
};
241-
237+
AsyncComponent.displayName = name || 'AsyncComponent';
242238
AsyncComponent.contextTypes = {
243239
asyncComponentsAncestor: _react2.default.PropTypes.shape({
244240
isBoundary: _react2.default.PropTypes.bool
@@ -249,8 +245,12 @@ function asyncComponent(args) {
249245
shouldRehydrate: _react2.default.PropTypes.func.isRequired
250246
})
251247
};
248+
AsyncComponent.childContextTypes = {
249+
asyncComponentsAncestor: _react2.default.PropTypes.shape({
250+
isBoundary: _react2.default.PropTypes.bool
251+
})
252+
};
252253

253-
AsyncComponent.displayName = name || 'AsyncComponent';
254254

255255
return AsyncComponent;
256256
}

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
"babel-eslint": "7.2.1",
6565
"babel-jest": "19.0.0",
6666
"babel-loader": "6.4.1",
67+
"babel-plugin-transform-class-properties": "6.23.0",
6768
"babel-polyfill": "6.23.0",
6869
"babel-preset-env": "1.3.2",
6970
"babel-preset-latest": "6.24.0",
@@ -92,7 +93,7 @@
9293
"ramda": "0.23.0",
9394
"react": "15.4.2",
9495
"react-addons-test-utils": "15.4.2",
95-
"react-async-bootstrapper": "^1.0.0",
96+
"react-async-bootstrapper": "^1.0.1",
9697
"react-dom": "15.4.2",
9798
"readline-sync": "1.4.7",
9899
"rimraf": "2.6.1",

src/AsyncComponentProvider.js

+27-27
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,33 @@ import React from 'react'
33
import createAsyncContext from './createAsyncContext'
44

55
class AsyncComponentProvider extends React.Component {
6+
static propTypes = {
7+
children: React.PropTypes.node.isRequired,
8+
asyncContext: React.PropTypes.shape({
9+
getNextId: React.PropTypes.func.isRequired,
10+
resolved: React.PropTypes.func.isRequired,
11+
getState: React.PropTypes.func.isRequired,
12+
}),
13+
rehydrateState: React.PropTypes.shape({
14+
resolved: React.PropTypes.object,
15+
}),
16+
};
17+
18+
static defaultProps = {
19+
asyncContext: undefined,
20+
rehydrateState: {
21+
resolved: {},
22+
},
23+
};
24+
25+
static childContextTypes = {
26+
asyncComponents: React.PropTypes.shape({
27+
getNextId: React.PropTypes.func.isRequired,
28+
resolved: React.PropTypes.func.isRequired,
29+
shouldRehydrate: React.PropTypes.func.isRequired,
30+
}).isRequired,
31+
};
32+
633
componentWillMount() {
734
this.asyncContext = this.props.asyncContext || createAsyncContext()
835
this.rehydrateState = this.props.rehydrateState
@@ -27,31 +54,4 @@ class AsyncComponentProvider extends React.Component {
2754
}
2855
}
2956

30-
AsyncComponentProvider.propTypes = {
31-
children: React.PropTypes.node.isRequired,
32-
asyncContext: React.PropTypes.shape({
33-
getNextId: React.PropTypes.func.isRequired,
34-
resolved: React.PropTypes.func.isRequired,
35-
getState: React.PropTypes.func.isRequired,
36-
}),
37-
rehydrateState: React.PropTypes.shape({
38-
resolved: React.PropTypes.object,
39-
}),
40-
}
41-
42-
AsyncComponentProvider.defaultProps = {
43-
asyncContext: undefined,
44-
rehydrateState: {
45-
resolved: {},
46-
},
47-
}
48-
49-
AsyncComponentProvider.childContextTypes = {
50-
asyncComponents: React.PropTypes.shape({
51-
getNextId: React.PropTypes.func.isRequired,
52-
resolved: React.PropTypes.func.isRequired,
53-
shouldRehydrate: React.PropTypes.func.isRequired,
54-
}).isRequired,
55-
}
56-
5757
export default AsyncComponentProvider

0 commit comments

Comments
 (0)