Open
Show file tree
Hide file tree
Changes from all commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Failed to load files.
Original file line numberDiff line numberDiff line change
Expand Up@@ -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
Expand DownExpand Up@@ -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) => <GeneralIcon svgContent={svgContent} {...props} />,
name: 'AsyncSvgIcon',
})
```

##### Named chunks

```jsx
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -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. = Object.create(superClass && superClass., { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setOf ? Object.setOf(subClass, superClass) : subClass.__proto__ = superClass; }

var validSSRModes = ['resolve', 'defer', 'boundary'];
var staticModuleId = Symbol();

function asyncComponent(config) {
var name = config.name,
Expand All@@ -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) {
Expand All@@ -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.
Expand All@@ -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);
Expand DownExpand Up@@ -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);
}
Expand All@@ -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;
Expand All@@ -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() {
Expand All@@ -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) {
Expand All@@ -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;
}
}]);

Expand Down
Loading