diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..259cf3d
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,20 @@
+# EditorConfig helps developers define and maintain consistent
+# coding styles between different editors and IDEs
+# http://editorconfig.org
+
+root = true
+
+[*]
+
+# Change these settings to your own preference
+indent_style = space
+indent_size = 2
+
+# We recommend you to keep these unchanged
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/README.md b/README.md
index d97d0c8..3c81d18 100644
--- a/README.md
+++ b/README.md
@@ -101,6 +101,8 @@ The asynchronous component factory. Config goes in, an asynchronous component co
- `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`.
- `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.
- `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. 😀
+ - `getModuleId` (_(props) => String_, Optional, default: internal static id) : Function which gathers id, when `resolve` can return different modules. All props will be passed to it.
+ - `render` (_(module, props) => Component_, Optional, default: `null`) : Function to use resolved module and render something with it. All props and resolved module for current id will be passed to it.
- `env` (_String_, Optional) : Provide either `'node'` or `'browser'` so you can write your own environment detection. Especially useful when using PhantomJS or ElectronJS to prerender the React App.
- `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.
- __`'resolve'`__ - Your asynchronous component will be resolved and rendered on the server. It's children will
@@ -133,6 +135,17 @@ export default asyncComponent({
})
```
+##### `render`
+
+```jsx
+export default asyncComponent({
+ resolve: (props) => import(`assets/images/icons/${props.iconName}.svg`),
+ getModuleId: (props) => props.iconName,
+ render: (svgContent, props) => ,
+ name: 'AsyncSvgIcon',
+})
+```
+
##### Named chunks
```jsx
diff --git a/commonjs/asyncComponent.js b/commonjs/asyncComponent.js
index 375eced..8ca3afc 100644
--- a/commonjs/asyncComponent.js
+++ b/commonjs/asyncComponent.js
@@ -27,6 +27,7 @@ function _possibleConstructorReturn(self, call) { if (!self) { throw new Referen
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; }
var validSSRModes = ['resolve', 'defer', 'boundary'];
+var staticModuleId = Symbol();
function asyncComponent(config) {
var name = config.name,
@@ -36,7 +37,12 @@ function asyncComponent(config) {
_config$serverMode = config.serverMode,
serverMode = _config$serverMode === undefined ? 'resolve' : _config$serverMode,
LoadingComponent = config.LoadingComponent,
- ErrorComponent = config.ErrorComponent;
+ ErrorComponent = config.ErrorComponent,
+ _render = config.render,
+ _config$getModuleId = config.getModuleId,
+ getModuleId = _config$getModuleId === undefined ? function () {
+ return staticModuleId;
+ } : _config$getModuleId;
if (validSSRModes.indexOf(serverMode) === -1) {
@@ -52,7 +58,7 @@ function asyncComponent(config) {
// This will be use to hold the resolved module allowing sharing across
// instances.
// NOTE: When using React Hot Loader this reference will become null.
- module: null,
+ modules: {},
// If an error occurred during a resolution it will be stored here.
error: null,
// Allows us to share the resolver promise across instances.
@@ -64,12 +70,15 @@ function asyncComponent(config) {
return autoResolveES2015Default && x != null && (typeof x === 'function' || (typeof x === 'undefined' ? 'undefined' : _typeof(x)) === 'object') && x.default ? x.default : x;
};
- var getResolver = function getResolver() {
- if (sharedState.resolver == null) {
+ var getResolver = function getResolver(props) {
+ // FIXME can we make "sharedState.resover" like "sharedState.modules" to get rid of `getModuleId(props) !== staticModuleId`?
+ // Because atm it calls `resolver` two times on first render. This is no problem for dynamic `import` thing of webpack,
+ // but other promise based api.
+ if (sharedState.resolver == null || getModuleId(props) !== staticModuleId) {
try {
// Wrap whatever the user returns in Promise.resolve to ensure a Promise
// is always returned.
- var resolver = resolve();
+ var resolver = resolve(props);
sharedState.resolver = Promise.resolve(resolver);
} catch (err) {
sharedState.resolver = Promise.reject(err);
@@ -142,9 +151,7 @@ function asyncComponent(config) {
}, {
key: 'componentWillMount',
value: function componentWillMount() {
- this.setState({
- module: sharedState.module
- });
+ this.setState({ modules: sharedState.modules });
if (sharedState.error) {
this.registerErrorState(sharedState.error);
}
@@ -159,27 +166,28 @@ function asyncComponent(config) {
}, {
key: 'shouldResolve',
value: function shouldResolve() {
- return sharedState.module == null && sharedState.error == null && !this.resolving && typeof window !== 'undefined';
+ return sharedState.modules[getModuleId(this.props)] !== null && sharedState.error == null && !this.resolving && typeof window !== 'undefined';
}
}, {
key: 'resolveModule',
value: function resolveModule() {
var _this3 = this;
+ var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.props;
+
this.resolving = true;
- return getResolver().then(function (module) {
+ var moduleId = getModuleId(props);
+ return getResolver(props).then(function (module) {
if (_this3.unmounted) {
return undefined;
}
if (_this3.context.asyncComponents != null) {
_this3.context.asyncComponents.resolved(sharedState.id);
}
- sharedState.module = module;
+ sharedState.modules[moduleId] = module;
if (env === 'browser') {
- _this3.setState({
- module: module
- });
+ _this3.setState({ modules: sharedState.modules });
}
_this3.resolving = false;
return module;
@@ -201,6 +209,16 @@ function asyncComponent(config) {
return undefined;
});
}
+ }, {
+ key: 'componentWillReceiveProps',
+ value: function componentWillReceiveProps(nextProps) {
+ var lastModuleId = getModuleId(this.props);
+ var nextModuleId = getModuleId(nextProps);
+ if (lastModuleId !== nextModuleId && !sharedState.modules[nextModuleId]) {
+ // FIXME add LoadingComponent logic to show old module for X ms (to prevent flash of content) and then show a loading component till the new module is loaded, make this configurable as for some cases it makes no sense to show the old content, like for icons
+ this.resolveModule(nextProps);
+ }
+ }
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
@@ -225,7 +243,7 @@ function asyncComponent(config) {
key: 'render',
value: function render() {
var _state = this.state,
- module = _state.module,
+ modules = _state.modules,
error = _state.error;
if (error) {
@@ -240,8 +258,9 @@ function asyncComponent(config) {
this.resolveModule();
}
- var Component = es6Resolve(module);
- return Component ? _react2.default.createElement(Component, this.props) : LoadingComponent ? _react2.default.createElement(LoadingComponent, this.props) : null;
+ var Component = es6Resolve(modules[getModuleId(this.props)]);
+ // eslint-disable-next-line no-nested-ternary
+ return Component ? _render ? _render(Component, this.props) : _react2.default.createElement(Component, this.props) : LoadingComponent ? _react2.default.createElement(LoadingComponent, this.props) : null;
}
}]);
diff --git a/package-lock.json b/package-lock.json
index 14664ea..0885f22 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1703,7 +1703,6 @@
"requires": {
"anymatch": "1.3.2",
"async-each": "1.0.1",
- "fsevents": "1.1.2",
"glob-parent": "2.0.0",
"inherits": "2.0.3",
"is-binary-path": "1.0.1",
@@ -3246,905 +3245,6 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
- "fsevents": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.1.2.tgz",
- "integrity": "sha512-Sn44E5wQW4bTHXvQmvSHwqbuiXtduD6Rrjm2ZtUEGbyrig+nUH3t/QD4M4/ZXViY556TBpRgZkHLDx3JxPwxiw==",
- "dev": true,
- "optional": true,
- "requires": {
- "nan": "2.7.0",
- "node-pre-gyp": "0.6.36"
- },
- "dependencies": {
- "abbrev": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ajv": {
- "version": "4.11.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "co": "4.6.0",
- "json-stable-stringify": "1.0.1"
- }
- },
- "ansi-regex": {
- "version": "2.1.1",
- "bundled": true,
- "dev": true
- },
- "aproba": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "are-we-there-yet": {
- "version": "1.1.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "delegates": "1.0.0",
- "readable-stream": "2.2.9"
- }
- },
- "asn1": {
- "version": "0.2.3",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "assert-plus": {
- "version": "0.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "asynckit": {
- "version": "0.4.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "aws-sign2": {
- "version": "0.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "aws4": {
- "version": "1.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "balanced-match": {
- "version": "0.4.2",
- "bundled": true,
- "dev": true
- },
- "bcrypt-pbkdf": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "tweetnacl": "0.14.5"
- }
- },
- "block-stream": {
- "version": "0.0.9",
- "bundled": true,
- "dev": true,
- "requires": {
- "inherits": "2.0.3"
- }
- },
- "boom": {
- "version": "2.10.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "hoek": "2.16.3"
- }
- },
- "brace-expansion": {
- "version": "1.1.7",
- "bundled": true,
- "dev": true,
- "requires": {
- "balanced-match": "0.4.2",
- "concat-map": "0.0.1"
- }
- },
- "buffer-shims": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true
- },
- "caseless": {
- "version": "0.12.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "co": {
- "version": "4.6.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "code-point-at": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true
- },
- "combined-stream": {
- "version": "1.0.5",
- "bundled": true,
- "dev": true,
- "requires": {
- "delayed-stream": "1.0.0"
- }
- },
- "concat-map": {
- "version": "0.0.1",
- "bundled": true,
- "dev": true
- },
- "console-control-strings": {
- "version": "1.1.0",
- "bundled": true,
- "dev": true
- },
- "core-util-is": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true
- },
- "cryptiles": {
- "version": "2.0.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "boom": "2.10.1"
- }
- },
- "dashdash": {
- "version": "1.14.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "assert-plus": "1.0.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- },
- "debug": {
- "version": "2.6.8",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "deep-extend": {
- "version": "0.4.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "delayed-stream": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true
- },
- "delegates": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "ecc-jsbn": {
- "version": "0.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "jsbn": "0.1.1"
- }
- },
- "extend": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "extsprintf": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true
- },
- "forever-agent": {
- "version": "0.6.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "form-data": {
- "version": "2.1.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "asynckit": "0.4.0",
- "combined-stream": "1.0.5",
- "mime-types": "2.1.15"
- }
- },
- "fs.realpath": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true
- },
- "fstream": {
- "version": "1.0.11",
- "bundled": true,
- "dev": true,
- "requires": {
- "graceful-fs": "4.1.11",
- "inherits": "2.0.3",
- "mkdirp": "0.5.1",
- "rimraf": "2.6.1"
- }
- },
- "fstream-ignore": {
- "version": "1.0.5",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "fstream": "1.0.11",
- "inherits": "2.0.3",
- "minimatch": "3.0.4"
- }
- },
- "gauge": {
- "version": "2.7.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "aproba": "1.1.1",
- "console-control-strings": "1.1.0",
- "has-unicode": "2.0.1",
- "object-assign": "4.1.1",
- "signal-exit": "3.0.2",
- "string-width": "1.0.2",
- "strip-ansi": "3.0.1",
- "wide-align": "1.1.2"
- }
- },
- "getpass": {
- "version": "0.1.7",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "assert-plus": "1.0.0"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- },
- "glob": {
- "version": "7.1.2",
- "bundled": true,
- "dev": true,
- "requires": {
- "fs.realpath": "1.0.0",
- "inflight": "1.0.6",
- "inherits": "2.0.3",
- "minimatch": "3.0.4",
- "once": "1.4.0",
- "path-is-absolute": "1.0.1"
- }
- },
- "graceful-fs": {
- "version": "4.1.11",
- "bundled": true,
- "dev": true
- },
- "har-schema": {
- "version": "1.0.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "har-validator": {
- "version": "4.2.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "ajv": "4.11.8",
- "har-schema": "1.0.5"
- }
- },
- "has-unicode": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "hawk": {
- "version": "3.1.3",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "boom": "2.10.1",
- "cryptiles": "2.0.5",
- "hoek": "2.16.3",
- "sntp": "1.0.9"
- }
- },
- "hoek": {
- "version": "2.16.3",
- "bundled": true,
- "dev": true
- },
- "http-signature": {
- "version": "1.1.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "assert-plus": "0.2.0",
- "jsprim": "1.4.0",
- "sshpk": "1.13.0"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "bundled": true,
- "dev": true,
- "requires": {
- "once": "1.4.0",
- "wrappy": "1.0.2"
- }
- },
- "inherits": {
- "version": "2.0.3",
- "bundled": true,
- "dev": true
- },
- "ini": {
- "version": "1.3.4",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "is-fullwidth-code-point": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "requires": {
- "number-is-nan": "1.0.1"
- }
- },
- "is-typedarray": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "isarray": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true
- },
- "isstream": {
- "version": "0.1.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "jodid25519": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "jsbn": "0.1.1"
- }
- },
- "jsbn": {
- "version": "0.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "json-schema": {
- "version": "0.2.3",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "json-stable-stringify": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "jsonify": "0.0.0"
- }
- },
- "json-stringify-safe": {
- "version": "5.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "jsonify": {
- "version": "0.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "jsprim": {
- "version": "1.4.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "assert-plus": "1.0.0",
- "extsprintf": "1.0.2",
- "json-schema": "0.2.3",
- "verror": "1.3.6"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- },
- "mime-db": {
- "version": "1.27.0",
- "bundled": true,
- "dev": true
- },
- "mime-types": {
- "version": "2.1.15",
- "bundled": true,
- "dev": true,
- "requires": {
- "mime-db": "1.27.0"
- }
- },
- "minimatch": {
- "version": "3.0.4",
- "bundled": true,
- "dev": true,
- "requires": {
- "brace-expansion": "1.1.7"
- }
- },
- "minimist": {
- "version": "0.0.8",
- "bundled": true,
- "dev": true
- },
- "mkdirp": {
- "version": "0.5.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "minimist": "0.0.8"
- }
- },
- "ms": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "node-pre-gyp": {
- "version": "0.6.36",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "mkdirp": "0.5.1",
- "nopt": "4.0.1",
- "npmlog": "4.1.0",
- "rc": "1.2.1",
- "request": "2.81.0",
- "rimraf": "2.6.1",
- "semver": "5.3.0",
- "tar": "2.2.1",
- "tar-pack": "3.4.0"
- }
- },
- "nopt": {
- "version": "4.0.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "abbrev": "1.1.0",
- "osenv": "0.1.4"
- }
- },
- "npmlog": {
- "version": "4.1.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "are-we-there-yet": "1.1.4",
- "console-control-strings": "1.1.0",
- "gauge": "2.7.4",
- "set-blocking": "2.0.0"
- }
- },
- "number-is-nan": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true
- },
- "oauth-sign": {
- "version": "0.8.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "object-assign": {
- "version": "4.1.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "once": {
- "version": "1.4.0",
- "bundled": true,
- "dev": true,
- "requires": {
- "wrappy": "1.0.2"
- }
- },
- "os-homedir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "os-tmpdir": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "osenv": {
- "version": "0.1.4",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "os-homedir": "1.0.2",
- "os-tmpdir": "1.0.2"
- }
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true
- },
- "performance-now": {
- "version": "0.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "process-nextick-args": {
- "version": "1.0.7",
- "bundled": true,
- "dev": true
- },
- "punycode": {
- "version": "1.4.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "qs": {
- "version": "6.4.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "rc": {
- "version": "1.2.1",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "deep-extend": "0.4.2",
- "ini": "1.3.4",
- "minimist": "1.2.0",
- "strip-json-comments": "2.0.1"
- },
- "dependencies": {
- "minimist": {
- "version": "1.2.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- },
- "readable-stream": {
- "version": "2.2.9",
- "bundled": true,
- "dev": true,
- "requires": {
- "buffer-shims": "1.0.0",
- "core-util-is": "1.0.2",
- "inherits": "2.0.3",
- "isarray": "1.0.0",
- "process-nextick-args": "1.0.7",
- "string_decoder": "1.0.1",
- "util-deprecate": "1.0.2"
- }
- },
- "request": {
- "version": "2.81.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "aws-sign2": "0.6.0",
- "aws4": "1.6.0",
- "caseless": "0.12.0",
- "combined-stream": "1.0.5",
- "extend": "3.0.1",
- "forever-agent": "0.6.1",
- "form-data": "2.1.4",
- "har-validator": "4.2.1",
- "hawk": "3.1.3",
- "http-signature": "1.1.1",
- "is-typedarray": "1.0.0",
- "isstream": "0.1.2",
- "json-stringify-safe": "5.0.1",
- "mime-types": "2.1.15",
- "oauth-sign": "0.8.2",
- "performance-now": "0.2.0",
- "qs": "6.4.0",
- "safe-buffer": "5.0.1",
- "stringstream": "0.0.5",
- "tough-cookie": "2.3.2",
- "tunnel-agent": "0.6.0",
- "uuid": "3.0.1"
- }
- },
- "rimraf": {
- "version": "2.6.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "glob": "7.1.2"
- }
- },
- "safe-buffer": {
- "version": "5.0.1",
- "bundled": true,
- "dev": true
- },
- "semver": {
- "version": "5.3.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "set-blocking": {
- "version": "2.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "signal-exit": {
- "version": "3.0.2",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "sntp": {
- "version": "1.0.9",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "hoek": "2.16.3"
- }
- },
- "sshpk": {
- "version": "1.13.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "asn1": "0.2.3",
- "assert-plus": "1.0.0",
- "bcrypt-pbkdf": "1.0.1",
- "dashdash": "1.14.1",
- "ecc-jsbn": "0.1.1",
- "getpass": "0.1.7",
- "jodid25519": "1.0.2",
- "jsbn": "0.1.1",
- "tweetnacl": "0.14.5"
- },
- "dependencies": {
- "assert-plus": {
- "version": "1.0.0",
- "bundled": true,
- "dev": true,
- "optional": true
- }
- }
- },
- "string-width": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true,
- "requires": {
- "code-point-at": "1.1.0",
- "is-fullwidth-code-point": "1.0.0",
- "strip-ansi": "3.0.1"
- }
- },
- "string_decoder": {
- "version": "1.0.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "safe-buffer": "5.0.1"
- }
- },
- "stringstream": {
- "version": "0.0.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "strip-ansi": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "ansi-regex": "2.1.1"
- }
- },
- "strip-json-comments": {
- "version": "2.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "tar": {
- "version": "2.2.1",
- "bundled": true,
- "dev": true,
- "requires": {
- "block-stream": "0.0.9",
- "fstream": "1.0.11",
- "inherits": "2.0.3"
- }
- },
- "tar-pack": {
- "version": "3.4.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "debug": "2.6.8",
- "fstream": "1.0.11",
- "fstream-ignore": "1.0.5",
- "once": "1.4.0",
- "readable-stream": "2.2.9",
- "rimraf": "2.6.1",
- "tar": "2.2.1",
- "uid-number": "0.0.6"
- }
- },
- "tough-cookie": {
- "version": "2.3.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "punycode": "1.4.1"
- }
- },
- "tunnel-agent": {
- "version": "0.6.0",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "safe-buffer": "5.0.1"
- }
- },
- "tweetnacl": {
- "version": "0.14.5",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "uid-number": {
- "version": "0.0.6",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "util-deprecate": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true
- },
- "uuid": {
- "version": "3.0.1",
- "bundled": true,
- "dev": true,
- "optional": true
- },
- "verror": {
- "version": "1.3.6",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "extsprintf": "1.0.2"
- }
- },
- "wide-align": {
- "version": "1.1.2",
- "bundled": true,
- "dev": true,
- "optional": true,
- "requires": {
- "string-width": "1.0.2"
- }
- },
- "wrappy": {
- "version": "1.0.2",
- "bundled": true,
- "dev": true
- }
- }
- },
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -6433,13 +5533,6 @@
"integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
"dev": true
},
- "nan": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/nan/-/nan-2.7.0.tgz",
- "integrity": "sha1-2Vv3IeyHfgjbJ27T/G63j5CDrUY=",
- "dev": true,
- "optional": true
- },
"native-promise-only": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/native-promise-only/-/native-promise-only-0.8.1.tgz",
@@ -7699,7 +6792,6 @@
"anymatch": "1.3.2",
"exec-sh": "0.2.1",
"fb-watchman": "2.0.0",
- "fsevents": "1.1.2",
"minimatch": "3.0.4",
"minimist": "1.2.0",
"walker": "1.0.7",
diff --git a/src/__tests__/__snapshots__/asyncComponent.test.js.snap b/src/__tests__/__snapshots__/asyncComponent.test.js.snap
index e3b0e7b..f249962 100644
--- a/src/__tests__/__snapshots__/asyncComponent.test.js.snap
+++ b/src/__tests__/__snapshots__/asyncComponent.test.js.snap
@@ -2,4 +2,34 @@
exports[`asyncComponent in a browser environment when an error occurs resolving a component should render the ErrorComponent 1`] = `"
failed to resolve
"`;
+exports[`asyncComponent in a browser environment when resolving dynamic imports should resolve module again on module id change 1`] = `
+
+
+
+ barComponent:Â
+ test
+
+
+
+`;
+
+exports[`asyncComponent in a browser environment when resolving dynamic imports should resolve module again on module id change 2`] = `
+
+
+
+ fooComponent:Â
+ foo
+
+
+
+`;
+
exports[`asyncComponent in a server environment when an error occurs resolving a component should not render the ErrorComponent 1`] = `null`;
diff --git a/src/__tests__/__snapshots__/integration.test.js.snap b/src/__tests__/__snapshots__/integration.test.js.snap
index e054e5d..c4c13ef 100644
--- a/src/__tests__/__snapshots__/integration.test.js.snap
+++ b/src/__tests__/__snapshots__/integration.test.js.snap
@@ -1,5 +1,115 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`integration tests browser rendering can resolve dynamic components - bar 1`] = `
+
+
+
+`;
+
+exports[`integration tests browser rendering can resolve dynamic components - bar 2`] = `
+
+
+
+
+
+ barComponent:Â
+ test
+
+
+
+
+
+`;
+
+exports[`integration tests browser rendering can resolve dynamic components - foo 1`] = `
+
+
+
+`;
+
+exports[`integration tests browser rendering can resolve dynamic components - foo 2`] = `
+
+
+
+
+
+ fooComponent:Â
+ foo
+
+
+
+
+
+`;
+
exports[`integration tests browser rendering renders the ErrorComponent 1`] = `
`;
+exports[`integration tests browser rendering renders the LoadingComponent 2`] = `
+
+
+
+
+ foo
+
+
+
+
+`;
+
exports[`integration tests render server and client 1`] = `"In Render.
DeferredAsyncBob Loading
In Boundary but outside an AsyncComponent, server render me!
"`;
exports[`integration tests render server and client 2`] = `
diff --git a/src/__tests__/asyncComponent.test.js b/src/__tests__/asyncComponent.test.js
index 3c0cc5d..4a1756d 100644
--- a/src/__tests__/asyncComponent.test.js
+++ b/src/__tests__/asyncComponent.test.js
@@ -24,7 +24,7 @@ describe('asyncComponent', () => {
describe('in a browser environment', () => {
describe('when an error occurs resolving a component', () => {
- it.only('should render the ErrorComponent', async () => {
+ it('should render the ErrorComponent', async () => {
const Bob = asyncComponent({
resolve: () => Promise.reject(new Error('failed to resolve')),
ErrorComponent: ({ error }) => {error.message}
,
@@ -35,6 +35,41 @@ describe('asyncComponent', () => {
expect(renderWrapper.html()).toMatchSnapshot()
})
})
+ describe('when resolving dynamic imports', () => {
+ it('should resolve module again on module id change', async () => {
+ const Dynamic = asyncComponent({
+ getModuleId: props => props.name,
+ resolve: props => {
+ if (props.name === 'foo') {
+ return ({ name }) => fooComponent: {name}
+ } else {
+ return ({ name }) => barComponent: {name}
+ }
+ },
+ render: (ResolvedComponent, props) => (
+
+ ),
+ env: 'browser',
+ })
+
+ const renderWrapper = mount()
+ await new Promise(resolve => setTimeout(resolve, errorResolveDelay))
+ renderWrapper.update()
+ expect(renderWrapper).toMatchSnapshot()
+ await new Promise(resolve => {
+ renderWrapper.setProps(
+ {
+ name: 'foo',
+ },
+ () => {
+ setTimeout(resolve, errorResolveDelay)
+ },
+ )
+ })
+ renderWrapper.update()
+ expect(renderWrapper).toMatchSnapshot()
+ })
+ })
})
describe('in a server environment', () => {
diff --git a/src/__tests__/integration.test.js b/src/__tests__/integration.test.js
index 2f9ccc2..5f66150 100644
--- a/src/__tests__/integration.test.js
+++ b/src/__tests__/integration.test.js
@@ -199,6 +199,58 @@ describe('integration tests', () => {
.then(() => expect(resolveCount).toEqual(1))
})
+ const Dynamic = asyncComponent({
+ getModuleId: props => props.name,
+ resolve: props => {
+ if (props.name === 'foo') {
+ return ({ name }) => fooComponent: {name}
+ } else {
+ return ({ name }) => barComponent: {name}
+ }
+ },
+ render: (ResolvedComponent, props) => (
+
+
+
+ ),
+ })
+
+ it('can resolve dynamic components - bar', () => {
+ const app = () => (
+
+
+
+ )
+
+ return asyncBootstrapper(app())
+ .then(() => mount(app()))
+ .then(render => {
+ // async component with props
+ expect(render).toMatchSnapshot()
+ // resolve to bar
+ render.update()
+ expect(render).toMatchSnapshot()
+ })
+ })
+
+ it.only('can resolve dynamic components - foo', () => {
+ const app = () => (
+
+
+
+ )
+
+ return asyncBootstrapper(app())
+ .then(() => mount(app()))
+ .then(render => {
+ // async component with props
+ expect(render).toMatchSnapshot()
+ // resolve to foo
+ render.update()
+ expect(render).toMatchSnapshot()
+ })
+ })
+
it('renders the LoadingComponent', () => {
const Foo = asyncComponent({
resolve: () =>
@@ -216,7 +268,15 @@ describe('integration tests', () => {
return asyncBootstrapper(app)
.then(() => mount(app))
- .then(render => expect(render).toMatchSnapshot())
+ .then(render => {
+ expect(render).toMatchSnapshot()
+ return new Promise(resolve => setTimeout(() => resolve(render), 150))
+ })
+ .then(render => {
+ render.update()
+ // rendered after loading
+ expect(render).toMatchSnapshot()
+ })
})
})
diff --git a/src/asyncComponent.js b/src/asyncComponent.js
index 75e0bd2..ee216c9 100644
--- a/src/asyncComponent.js
+++ b/src/asyncComponent.js
@@ -2,6 +2,7 @@ import React from 'react'
import PropTypes from 'prop-types'
const validSSRModes = ['resolve', 'defer', 'boundary']
+const staticModuleId = Symbol()
function asyncComponent(config) {
const {
@@ -11,6 +12,8 @@ function asyncComponent(config) {
serverMode = 'resolve',
LoadingComponent,
ErrorComponent,
+ render,
+ getModuleId = () => staticModuleId,
} = config
if (validSSRModes.indexOf(serverMode) === -1) {
@@ -29,7 +32,7 @@ function asyncComponent(config) {
// This will be use to hold the resolved module allowing sharing across
// instances.
// NOTE: When using React Hot Loader this reference will become null.
- module: null,
+ modules: {},
// If an error occurred during a resolution it will be stored here.
error: null,
// Allows us to share the resolver promise across instances.
@@ -46,12 +49,12 @@ function asyncComponent(config) {
? x.default
: x
- const getResolver = () => {
- if (sharedState.resolver == null) {
+ const getResolver = props => {
+ if (sharedState.resolver == null || getModuleId(props) !== staticModuleId) {
try {
// Wrap whatever the user returns in Promise.resolve to ensure a Promise
// is always returned.
- const resolver = resolve()
+ const resolver = resolve(props)
sharedState.resolver = Promise.resolve(resolver)
} catch (err) {
sharedState.resolver = Promise.reject(err)
@@ -106,9 +109,7 @@ function asyncComponent(config) {
}
componentWillMount() {
- this.setState({
- module: sharedState.module,
- })
+ this.setState({ modules: sharedState.modules })
if (sharedState.error) {
this.registerErrorState(sharedState.error)
}
@@ -122,17 +123,18 @@ function asyncComponent(config) {
shouldResolve() {
return (
- sharedState.module == null &&
+ sharedState.modules[getModuleId(this.props)] !== null &&
sharedState.error == null &&
!this.resolving &&
typeof window !== 'undefined'
)
}
- resolveModule() {
+ resolveModule(props = this.props) {
this.resolving = true
- return getResolver()
+ let moduleId = getModuleId(props)
+ return getResolver(props)
.then(module => {
if (this.unmounted) {
return undefined
@@ -140,11 +142,9 @@ function asyncComponent(config) {
if (this.context.asyncComponents != null) {
this.context.asyncComponents.resolved(sharedState.id)
}
- sharedState.module = module
+ sharedState.modules[moduleId] = module
if (env === 'browser') {
- this.setState({
- module,
- })
+ this.setState({ modules: sharedState.modules })
}
this.resolving = false
return module
@@ -168,6 +168,14 @@ function asyncComponent(config) {
})
}
+ componentWillReceiveProps(nextProps) {
+ let lastModuleId = getModuleId(this.props)
+ let nextModuleId = getModuleId(nextProps)
+ if (lastModuleId !== nextModuleId && !sharedState.modules[nextModuleId]) {
+ this.resolveModule(nextProps)
+ }
+ }
+
componentWillUnmount() {
this.unmounted = true
}
@@ -185,7 +193,7 @@ function asyncComponent(config) {
}
render() {
- const { module, error } = this.state
+ const { modules, error } = this.state
if (error) {
return ErrorComponent ? (
@@ -200,9 +208,14 @@ function asyncComponent(config) {
this.resolveModule()
}
- const Component = es6Resolve(module)
+ const Component = es6Resolve(modules[getModuleId(this.props)])
+ // eslint-disable-next-line no-nested-ternary
return Component ? (
-
+ render ? (
+ render(Component, this.props)
+ ) : (
+
+ )
) : LoadingComponent ? (
) : null
diff --git a/umd/react-async-component.js b/umd/react-async-component.js
index 761fba7..ae041eb 100644
--- a/umd/react-async-component.js
+++ b/umd/react-async-component.js
@@ -288,6 +288,7 @@ function _possibleConstructorReturn(self, call) { if (!self) { throw new Referen
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; }
var validSSRModes = ['resolve', 'defer', 'boundary'];
+var staticModuleId = Symbol();
function asyncComponent(config) {
var name = config.name,
@@ -297,7 +298,12 @@ function asyncComponent(config) {
_config$serverMode = config.serverMode,
serverMode = _config$serverMode === undefined ? 'resolve' : _config$serverMode,
LoadingComponent = config.LoadingComponent,
- ErrorComponent = config.ErrorComponent;
+ ErrorComponent = config.ErrorComponent,
+ _render = config.render,
+ _config$getModuleId = config.getModuleId,
+ getModuleId = _config$getModuleId === undefined ? function () {
+ return staticModuleId;
+ } : _config$getModuleId;
if (validSSRModes.indexOf(serverMode) === -1) {
@@ -313,7 +319,7 @@ function asyncComponent(config) {
// This will be use to hold the resolved module allowing sharing across
// instances.
// NOTE: When using React Hot Loader this reference will become null.
- module: null,
+ modules: {},
// If an error occurred during a resolution it will be stored here.
error: null,
// Allows us to share the resolver promise across instances.
@@ -325,12 +331,15 @@ function asyncComponent(config) {
return autoResolveES2015Default && x != null && (typeof x === 'function' || (typeof x === 'undefined' ? 'undefined' : _typeof(x)) === 'object') && x.default ? x.default : x;
};
- var getResolver = function getResolver() {
- if (sharedState.resolver == null) {
+ var getResolver = function getResolver(props) {
+ // FIXME can we make "sharedState.resover" like "sharedState.modules" to get rid of `getModuleId(props) !== staticModuleId`?
+ // Because atm it calls `resolver` two times on first render. This is no problem for dynamic `import` thing of webpack,
+ // but other promise based api.
+ if (sharedState.resolver == null || getModuleId(props) !== staticModuleId) {
try {
// Wrap whatever the user returns in Promise.resolve to ensure a Promise
// is always returned.
- var resolver = resolve();
+ var resolver = resolve(props);
sharedState.resolver = Promise.resolve(resolver);
} catch (err) {
sharedState.resolver = Promise.reject(err);
@@ -403,9 +412,7 @@ function asyncComponent(config) {
}, {
key: 'componentWillMount',
value: function componentWillMount() {
- this.setState({
- module: sharedState.module
- });
+ this.setState({ modules: sharedState.modules });
if (sharedState.error) {
this.registerErrorState(sharedState.error);
}
@@ -420,27 +427,28 @@ function asyncComponent(config) {
}, {
key: 'shouldResolve',
value: function shouldResolve() {
- return sharedState.module == null && sharedState.error == null && !this.resolving && typeof window !== 'undefined';
+ return sharedState.modules[getModuleId(this.props)] !== null && sharedState.error == null && !this.resolving && typeof window !== 'undefined';
}
}, {
key: 'resolveModule',
value: function resolveModule() {
var _this3 = this;
+ var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : this.props;
+
this.resolving = true;
- return getResolver().then(function (module) {
+ var moduleId = getModuleId(props);
+ return getResolver(props).then(function (module) {
if (_this3.unmounted) {
return undefined;
}
if (_this3.context.asyncComponents != null) {
_this3.context.asyncComponents.resolved(sharedState.id);
}
- sharedState.module = module;
+ sharedState.modules[moduleId] = module;
if (env === 'browser') {
- _this3.setState({
- module: module
- });
+ _this3.setState({ modules: sharedState.modules });
}
_this3.resolving = false;
return module;
@@ -462,6 +470,16 @@ function asyncComponent(config) {
return undefined;
});
}
+ }, {
+ key: 'componentWillReceiveProps',
+ value: function componentWillReceiveProps(nextProps) {
+ var lastModuleId = getModuleId(this.props);
+ var nextModuleId = getModuleId(nextProps);
+ if (lastModuleId !== nextModuleId && !sharedState.modules[nextModuleId]) {
+ // FIXME add LoadingComponent logic to show old module for X ms (to prevent flash of content) and then show a loading component till the new module is loaded, make this configurable as for some cases it makes no sense to show the old content, like for icons
+ this.resolveModule(nextProps);
+ }
+ }
}, {
key: 'componentWillUnmount',
value: function componentWillUnmount() {
@@ -486,7 +504,7 @@ function asyncComponent(config) {
key: 'render',
value: function render() {
var _state = this.state,
- module = _state.module,
+ modules = _state.modules,
error = _state.error;
if (error) {
@@ -501,8 +519,9 @@ function asyncComponent(config) {
this.resolveModule();
}
- var Component = es6Resolve(module);
- return Component ? _react2.default.createElement(Component, this.props) : LoadingComponent ? _react2.default.createElement(LoadingComponent, this.props) : null;
+ var Component = es6Resolve(modules[getModuleId(this.props)]);
+ // eslint-disable-next-line no-nested-ternary
+ return Component ? _render ? _render(Component, this.props) : _react2.default.createElement(Component, this.props) : LoadingComponent ? _react2.default.createElement(LoadingComponent, this.props) : null;
}
}]);
diff --git a/umd/react-async-component.min.js b/umd/react-async-component.min.js
index 2382552..44c0332 100644
--- a/umd/react-async-component.min.js
+++ b/umd/react-async-component.min.js
@@ -1 +1 @@
-!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("react"),require("prop-types")):"function"==typeof define&&define.amd?define(["react","prop-types"],t):"object"==typeof exports?exports.ReactAsyncComponent=t(require("react"),require("prop-types")):e.ReactAsyncComponent=t(e.React,e.PropTypes)}(this,function(e,t){return function(e){function t(o){if(n[o])return n[o].exports;var r=n[o]={i:o,l:!1,exports:{}};return e[o].call(r.exports,r,r.exports,t),r.l=!0,r.exports}var n={};return t.m=e,t.c=n,t.d=function(e,n,o){t.o(e,n)||Object.defineProperty(e,n,{configurable:!1,enumerable:!0,get:o})},t.n=function(e){var n=e&&e.__esModule?function(){return e.default}:function(){return e};return t.d(n,"a",n),n},t.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},t.p="",t(t.s=3)}([function(t,n){t.exports=e},function(e,n){e.exports=t},function(e,t,n){"use strict";function o(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function r(){var e=0,t={};return{getNextId:function(){return e+=1},resolved:function(e){t[e]=!0},getState:function(){return{resolved:Object.keys(t).reduce(function(e,t){return Object.assign(e,o({},t,!0))},{})}}}}Object.defineProperty(t,"__esModule",{value:!0}),t.default=r},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0}),t.asyncComponent=t.createAsyncContext=t.AsyncComponentProvider=void 0;var r=n(4),u=o(r),s=n(2),i=o(s),l=n(5),a=o(l);t.AsyncComponentProvider=u.default,t.createAsyncContext=i.default,t.asyncComponent=a.default},function(e,t,n){"use strict";function o(e){return e&&e.__esModule?e:{default:e}}function r(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function u(e,t){if(!e)throw new ReferenceError("this hasn't been initialised - super() hasn't been called");return!t||"object"!=typeof t&&"function"!=typeof t?e:t}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(t,"__esModule",{value:!0});var i=function(){function e(e,t){for(var n=0;n-1?e.env:"undefined"==typeof window?"node":"browser",C={id:null,module:null,error:null,resolver:null},x=function(e){return i&&null!=e&&("function"==typeof e||"object"===(void 0===e?"undefined":c(e)))&&e.default?e.default:e},g=function(){if(null==C.resolver)try{var e=n();C.resolver=Promise.resolve(e)}catch(e){C.resolver=Promise.reject(e)}return C.resolver},w=function(e){function t(e,n){r(this,t);var o=u(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n));return null==o.context.asyncComponents||C.id||(C.id=o.context.asyncComponents.getNextId()),o}return s(t,e),a(t,[{key:"asyncBootstrap",value:function(){var e=this,t=this.context,n=t.asyncComponents,o=t.asyncComponentsAncestor,r=n.shouldRehydrate,u=function(){return e.resolveModule().then(function(e){return void 0!==e})};if("browser"===b)return!!r(C.id)&&u();var s=null!=o&&o.isBoundary;return"defer"!==p&&!s&&u()}},{key:"getChildContext",value:function(){return null==this.context.asyncComponents?{asyncComponentsAncestor:null}:{asyncComponentsAncestor:{isBoundary:"boundary"===p}}}},{key:"componentWillMount",value:function(){this.setState({module:C.module}),C.error&&this.registerErrorState(C.error)}},{key:"componentDidMount",value:function(){this.shouldResolve()&&this.resolveModule()}},{key:"shouldResolve",value:function(){return null==C.module&&null==C.error&&!this.resolving&&"undefined"!=typeof window}},{key:"resolveModule",value:function(){var e=this;return this.resolving=!0,g().then(function(t){if(!e.unmounted)return null!=e.context.asyncComponents&&e.context.asyncComponents.resolved(C.id),C.module=t,"browser"===b&&e.setState({module:t}),e.resolving=!1,t}).catch(function(t){e.unmounted||(("node"===b||"browser"===b&&!m)&&(console.warn("Failed to resolve asyncComponent"),console.warn(t)),C.error=t,e.registerErrorState(t),e.resolving=!1)})}},{key:"componentWillUnmount",value:function(){this.unmounted=!0}},{key:"registerErrorState",value:function(e){var t=this;"browser"===b&&setTimeout(function(){t.unmounted||t.setState({error:e})},16)}},{key:"render",value:function(){var e=this.state,t=e.module,n=e.error;if(n)return m?d.default.createElement(m,l({},this.props,{error:n})):null;this.shouldResolve()&&this.resolveModule();var o=x(t);return o?d.default.createElement(o,this.props):h?d.default.createElement(h,this.props):null}}]),t}(d.default.Component);return w.displayName=t||"AsyncComponent",w.contextTypes={asyncComponentsAncestor:y.default.shape({isBoundary:y.default.bool}),asyncComponents:y.default.shape({getNextId:y.default.func.isRequired,resolved:y.default.func.isRequired,shouldRehydrate:y.default.func.isRequired})},w.childContextTypes={asyncComponentsAncestor:y.default.shape({isBoundary:y.default.bool})},w}Object.defineProperty(t,"__esModule",{value:!0});var l=Object.assign||function(e){for(var t=1;t-1?e.env:"undefined"==typeof window?"node":"browser",_={id:null,modules:{},error:null,resolver:null},O=function(e){return i&&null!=e&&("function"==typeof e||"object"===(void 0===e?"undefined":c(e)))&&e.default?e.default:e},j=function(e){if(null==_.resolver||g(e)!==h)try{var t=n(e);_.resolver=Promise.resolve(t)}catch(e){_.resolver=Promise.reject(e)}return _.resolver},R=function(e){function t(e,n){r(this,t);var o=u(this,(t.__proto__||Object.getPrototypeOf(t)).call(this,e,n));return null==o.context.asyncComponents||_.id||(_.id=o.context.asyncComponents.getNextId()),o}return s(t,e),a(t,[{key:"asyncBootstrap",value:function(){var e=this,t=this.context,n=t.asyncComponents,o=t.asyncComponentsAncestor,r=n.shouldRehydrate,u=function(){return e.resolveModule().then(function(e){return void 0!==e})};if("browser"===w)return!!r(_.id)&&u();var s=null!=o&&o.isBoundary;return"defer"!==p&&!s&&u()}},{key:"getChildContext",value:function(){return null==this.context.asyncComponents?{asyncComponentsAncestor:null}:{asyncComponentsAncestor:{isBoundary:"boundary"===p}}}},{key:"componentWillMount",value:function(){this.setState({modules:_.modules}),_.error&&this.registerErrorState(_.error)}},{key:"componentDidMount",value:function(){this.shouldResolve()&&this.resolveModule()}},{key:"shouldResolve",value:function(){return null!==_.modules[g(this.props)]&&null==_.error&&!this.resolving&&"undefined"!=typeof window}},{key:"resolveModule",value:function(){var e=this,t=arguments.length>0&&void 0!==arguments[0]?arguments[0]:this.props;this.resolving=!0;var n=g(t);return j(t).then(function(t){if(!e.unmounted)return null!=e.context.asyncComponents&&e.context.asyncComponents.resolved(_.id),_.modules[n]=t,"browser"===w&&e.setState({modules:_.modules}),e.resolving=!1,t}).catch(function(t){e.unmounted||(("node"===w||"browser"===w&&!b)&&(console.warn("Failed to resolve asyncComponent"),console.warn(t)),_.error=t,e.registerErrorState(t),e.resolving=!1)})}},{key:"componentWillReceiveProps",value:function(e){var t=g(this.props),n=g(e);t===n||_.modules[n]||this.resolveModule(e)}},{key:"componentWillUnmount",value:function(){this.unmounted=!0}},{key:"registerErrorState",value:function(e){var t=this;"browser"===w&&setTimeout(function(){t.unmounted||t.setState({error:e})},16)}},{key:"render",value:function(){var e=this.state,t=e.modules,n=e.error;if(n)return b?d.default.createElement(b,l({},this.props,{error:n})):null;this.shouldResolve()&&this.resolveModule();var o=O(t[g(this.props)]);return o?C?C(o,this.props):d.default.createElement(o,this.props):m?d.default.createElement(m,this.props):null}}]),t}(d.default.Component);return R.displayName=t||"AsyncComponent",R.contextTypes={asyncComponentsAncestor:y.default.shape({isBoundary:y.default.bool}),asyncComponents:y.default.shape({getNextId:y.default.func.isRequired,resolved:y.default.func.isRequired,shouldRehydrate:y.default.func.isRequired})},R.childContextTypes={asyncComponentsAncestor:y.default.shape({isBoundary:y.default.bool})},R}Object.defineProperty(t,"__esModule",{value:!0});var l=Object.assign||function(e){for(var t=1;t