Skip to content

Instantly share code, notes, and snippets.

@acdlite
Last active January 20, 2023 08:23
Show Gist options
  • Save acdlite/a68433004f9d6b4cbc83b5cc3990c194 to your computer and use it in GitHub Desktop.
Save acdlite/a68433004f9d6b4cbc83b5cc3990c194 to your computer and use it in GitHub Desktop.
Quick and dirty code splitting with React Router v4
// getComponent is a function that returns a promise for a component
// It will not be called until the first mount
function asyncComponent(getComponent) {
return class AsyncComponent extends React.Component {
static Component = null;
state = { Component: AsyncComponent.Component };
componentWillMount() {
if (!this.state.Component) {
getComponent().then(Component => {
AsyncComponent.Component = Component
this.setState({ Component })
})
}
}
render() {
const { Component } = this.state
if (Component) {
return <Component {...this.props} />
}
return null
}
}
}
const Foo = asyncComponent(() =>
System.import('./Foo').then(module => module.default)
)
const Bar = asyncComponent(() =>
System.import('./Bar').then(module => module.default)
)
const App = () =>
<BrowserRouter>
<Link to="/foo">Foo</Link>
<Link to="/bar">Bar</Link>
<Match pattern="/foo" component={Foo} />
<Match pattern="/bar" component={Bar} />
</BrowserRouter>
export default App
@StefanoSega
Copy link

Hello!

Writing an async component like this in TypeScript:

export function routeAsyncComponent(getComponent: () => Promise<React.Component>) {
  interface IRouteAsyncComponentState {
    Component: React.Component;
  }

  return class RouteAsyncComponent extends React.Component<any, IRouteAsyncComponentState> {
    static Component: React.Component = null;
    state = { Component: RouteAsyncComponent.Component };

    componentWillMount() {
      if (!this.state.Component) {
        getComponent().then(Component => {
          RouteAsyncComponent.Component = Component;
          this.setState({ Component });
        });
      }
    }
    render() {
      const { Component } = this.state;
      if (Component) {
        return <Component {...this.props} />;
      }

      return null;
    }
  }
}

TypeScript complains on return <Component {...this.props} />; :

Type assertion using the '<>' syntax is forbidden. Use the 'as' syntax instead. (no-angle-bracket-type-assertion)tslint(1)

Type assertion on object literals is forbidden, use a type annotation instead. (no-object-literal-type-assertion)tslint(1)

Cannot find name 'Component'.ts(2304)

@BertieGo
Copy link

so dirty and so good, bro

@lynda0214
Copy link

Thank you for this brilliant code. I've been using this code very usefully.

Since System.import is deprecated, you have to use import.

Additionally, rather than doing module => module.default for every routes, you can remove some unnecessary codes by doing ({default: Component}) => ... in componentWillMount

Full Code:

function asyncComponent(getComponent) {
  return class AsyncComponent extends React.Component {
    static Component = null;
    state = { Component: AsyncComponent.Component };

    componentWillMount() {
      if (!this.state.Component) {
        getComponent().then(({default: Component}) => {
          AsyncComponent.Component = Component
          this.setState({ Component })
        })
      }
    }
    render() {
      const { Component } = this.state
      if (Component) {
        return <Component {...this.props} />
      }
      return null
    }
  }
}

const Foo = asyncComponent(() => import('./Foo'))
const Bar = asyncComponent(() => import('./Bar'))

Can't figure out why .default is needed

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment