Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Lazy loading a React component with TypeScript 2.2
// webpack hack, because typescript 2.2 doesn't support `import()`
// https://github.com/Microsoft/TypeScript/issues/12364#issuecomment-270819757
declare function _import<T>(path: string): Promise<T>;
// this is our single page application
import React from 'react';
import { render } from 'react-dom';
import { Router, Route, Switch} from 'react-router-dom';
import { Lazy } from 'react-lazy'; // package defined above
import { createHashHistory } from 'history';
//import { App } from './App'; // our app component - now loaded lazily!
const hashHistory = createHashHistory();
render(
<Router history={hashHistory}>
<Switch>
<Route exact path="/" render={() => <Lazy fetch={() => _import('./App')} />} />
{/* other routes */}
</Switch>
</Router>,
document.getElementById('app')
);
// this is our imaginary package "react-lazy"
import React, { Component, createElement } from 'react';
import { Redirect } from 'react-router-dom';
import { observer } from 'mobx-react';
import { observable } from 'mobx';
/**
* This is generic module interface. We assume that we can access React components.
*/
export interface FetchedModule {
[key: string]: (p: any) => JSX.Element;
}
export interface LazyProps {
/**
* A callback which will fetch your module. It could look like `() => _import('./home')`.
*
* Note that we currently need to write `_import`, because of this TypeScript bug:
* https://github.com/Microsoft/TypeScript/issues/12364
*/
fetch: () => Promise<FetchedModule>;
/**
* If no `componentName` is given, the default export will be used.
*/
componentName?: string;
/**
* The component which will be shown while the requested module will be loaded.
*/
loaderComponent?: JSX.Element;
/**
* The component which will be shown, if an error occured while the requested module was fetched.
*/
errorComponent?: JSX.Element;
/**
* All other props will be passed to the resolved component.
*/
[key: string]: any;
}
@observer
export class Lazy extends Component<LazyProps, void> {
static defaultProps: Partial<LazyProps> = {
componentName: 'default',
loaderComponent: <p>Loading...</p>,
errorComponent: <Redirect to="/error" />
};
@observable.ref
private result: FetchedModule | null = null;
@observable.ref
private error: any;
async componentDidMount() {
try {
this.result = await this.props.fetch();
} catch (e) {
this.error = e;
}
}
render() {
const {
fetch,
errorComponent,
componentName,
loaderComponent,
...compProps
} = this.props;
if (this.result) {
return createElement(this.result[componentName!], compProps);
} else if (this.error) {
return errorComponent!;
} else {
return loaderComponent!;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.