import * as _ from 'lodash'; import * as React from 'react'; interface LazyComponentProps { reactComponentPromise: Promise>; reactComponentProps: any; } interface LazyComponentState { component?: React.ComponentClass; } /** * This component is used for rendering components that are lazily loaded from other chunks. * Source: https://reacttraining.com/react-router/web/guides/code-splitting */ export class LazyComponent extends React.Component { constructor(props: LazyComponentProps) { super(props); this.state = { component: undefined, }; } public componentWillMount() { // tslint:disable-next-line:no-floating-promises this._loadComponentFireAndForgetAsync(this.props); } public componentWillReceiveProps(nextProps: LazyComponentProps) { if (nextProps.reactComponentPromise !== this.props.reactComponentPromise) { // tslint:disable-next-line:no-floating-promises this._loadComponentFireAndForgetAsync(nextProps); } } public render() { return _.isUndefined(this.state.component) ? null : React.createElement(this.state.component, this.props.reactComponentProps); } private async _loadComponentFireAndForgetAsync(props: LazyComponentProps) { const component = await props.reactComponentPromise; this.setState({ component, }); } } /** * [createLazyComponent description] * @param componentName name of exported component * @param lazyImport lambda returning module promise * we pass a lambda because we only want to require a module if it's used * @example `const LazyPortal = createLazyComponent('Portal', () => System.import('ts/containers/portal'));`` */ export const createLazyComponent = (componentName: string, lazyImport: () => Promise) => { return (props: any) => { const reactComponentPromise = (async (): Promise> => { const mod = await lazyImport(); const component = mod[componentName]; if (_.isUndefined(component)) { throw new Error(`Did not find exported component: ${componentName}`); } return component; })(); return ( ); }; };