Search code examples
javascriptreactjsreact-loadable

How can I use react-loadable with react-router?


I am trying to implement an application that has 10-15 pages. That works with react-router well but I should use react-loadable to have a Spinner and loading effect ...

But how can I import router components inside the loadable ?

I should create one const variable for each component ?

Like this :

const Home = Loadable({
    loader: () => import('./Home'),
    loading: Loading,
});

const News = Loadable({
    loader: () => import('./News'),
    loading: Loading,
});

const Gallery = Loadable({
    loader: () => import('./Gallery'),
    loading: Loading,
});

class App extends React.Component {
    render() {
        return (
          <Router>
              <Route exact path="/" component={Home} />
              <Route path="/news" component={News} />
              <Route path="/gallery" component={Gallery} />
          </Router>
        )
    }
}

Or it's possible with other tricks ?


Solution

  • This is a bit tricky. You don't need to use react-loadable.

    Demo here.

    If you want to make loader for images and other components until they are onLoad, you can use react-content-loader instead to create skeleton screens (See components/Image.js in demo). It could make "almost" perfect loader. So far, this is what I can do. I have no idea to detect onLoad for css :(

    You can create a new file named routes.js which includes all pages.

    - /src
    -- /pages
    --- Gallery.js
    --- Home.js
    --- News.js
    --- withBase.js
    -- App.js
    -- routes.js
    

    routes.js

    import Home from './pages/Home';
    import Gallery from './pages/Gallery';
    import News from './pages/News';
    // Add as much as you want...
    
    export default [
       {
           path: '/',
           component: Home,
       },
       {
           path: '/gallery',
           component: Gallery,
       },
       {
           path: '/news',
           component: News,
       },
    ];
    

    You need to create a high order component that will wrap each page.

    withBase.js (HOC)

    import React from "react";
    
    export default WrappedComponent =>
      class extends React.Component {
        state = {
          isLoading: true
        };
    
        componentDidMount() {
          this.hideLoader();
        }
    
        hideLoader = () => {
          // This is for demo purpose
          const proc = new Promise(resolve => {
            setTimeout(() => resolve(), 3000);
          });
          proc.then(() => this.setState({ isLoading: false }));
        };
    
        render() {
          return (
            <div>
              {this.state.isLoading ? <p>Loading...</p> : <WrappedComponent />}
            </div>
          );
        }
      };
    

    Usage: e.g. export default withBase(Home).

    Then, in App.js just loop the routes.

        <Switch>
          {routes.map(route => (
            <Route
              exact
              key={route.path}
              path={route.path}
              component={route.component}
            />
          ))}
        </Switch>