-
-
Save dugagjin/7eb0eaa6693a346594ae5b35691adc72 to your computer and use it in GitHub Desktop.
Lazy loading a React component with TypeScript 2.2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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 file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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