3

There is a new concept of code splitting and async routing using suspense and lazy introduced by react itself. With that concept, how can we show the progress bar at the top of the page when the route is changed. I could show the loading icon, text etc but not the progress bar(0 to 100%). Here is how i have done

const About = lazyLoading(() => import("./components/About"), {
  fallback: <h1>Loading...</h1>
});
const Home = lazyLoading(() => import("./components/Home"), {
  fallback: <h1>Loading...</h1>
});

const BasicExample = () => {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
        </ul>

        <hr />

        <Route exact path="/" render={() => <Home name="hello" />} />
        <Route path="/about" component={About} />
      </div>
    </Router>
  );
};
render(<BasicExample />, document.getElementById("root"));

lazyloading.js

const lazyloading = (importFunc, { fallback = null }) => {
  const LazyComponent = lazy(importFunc);
  return props => (
    <Suspense fallback={fallback}>
      <LazyComponent {...props} />
    </Suspense>
  );
};

lazyloading.defaultProps = {
  fallback: null
};

export default lazyloading;

Here is the example in codesandbox either where i have progress component too but did not have idea on how i implement it when using suspense and lazy in the way I am doing

https://codesandbox.io/s/zw7mx97293jav

milan
  • 2,409
  • 2
  • 34
  • 71

1 Answers1

6

You could just use ProgressBar from react-topbar-progress-indicator. You can never show the exact percentage as different component take different type to render depending on side-effects used - API call, timeout etc. but this component does great job of showing progress bar.

1) For About component, I have intentionally added timeout of 3s to see the progress bar in action.

2) You could configure Progress bar with different colors as well.

3) Codesandox link of working sample.

import React from "react";
import ReactDOM from "react-dom";
import lazyLoading from "./LazyLoading";
import ProgressBar from "react-topbar-progress-indicator";
import { BrowserRouter as Router, Link, Route } from "react-router-dom";

const About = lazyLoading(
  () => {
    return new Promise(resolve => {
      setTimeout(() => resolve(import("./About")), 3000);
    });
  },
  {
    fallback: <ProgressBar />
  }
);

const Home = lazyLoading(() => import("./Home"), {
  fallback: <ProgressBar />
});

const BasicExample = () => {
  return (
    <Router>
      <div>
        <ul>
          <li>
            <Link to="/">Home</Link>
          </li>
          <li>
            <Link to="/about">About</Link>
          </li>
        </ul>

        <hr />

        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
      </div>
    </Router>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<BasicExample />, rootElement);

Hope that helps!!!

tarzen chugh
  • 10,561
  • 4
  • 20
  • 30
  • First of all thanks for your solution. I think, if the about component is a bit heavy or takes time to load the data then it would work without using timeout too. Am i right? – milan Apr 28 '19 at 15:59
  • timeout was just to imitate the behaviour, you don't need timeout, `About` component will be same as `Home` component expect the path – tarzen chugh Apr 28 '19 at 16:02
  • Have anyone tried doing it with reach router? with reach router you can only wrap the entire router with suspense. can anyone help please? – Osama Ahmaro Sep 02 '19 at 07:02
  • @OsamaAhmaro I have tried it and it's working! (v.5, react 17) – testing_22 Jul 20 '23 at 01:36