© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2021
D. B. Duldulao, R. J. L. CabagnotPractical Enterprise Reacthttps://doi.org/10.1007/978-1-4842-6975-6_5

5. Navigating React Router

Devlin Basilan Duldulao1   and Ruby Jane Leyva Cabagnot1
(1)
Oslo, Norway
 
This chapter deals with navigation using React Router, a specific library for handling and managing routes within a web app, which we would do so as follows:
  1. 1.

    Create a JavaScript file, router.js, in the root directory. For a TS file, we will use router.tsx.

     
  2. 2.

    Use the Route from react-router-dom to define the path and component associated with the path, for example:

     
<Route path={"/hello"} component={HelloComponent}/>
  1. 3.

    Wrap all the Route that we created inside the Switch from react-router-dom, for example:

     
<Switch><Router .../> and more...</Switch>
  1. 4.

    Navigate successfully to any page using the Link from react-router-dom, for example:

     
<Link to="/hello">Hello</Link>

Along the way, we will learn how to use our preferred design library in the application we are building, which is Material-UI.

We will also touch on lazy loading or code-splitting our component to speed up initial loading time and improve user experience. In many relatively small React SPAs (single-page applications), the concept of code-splitting may not be necessary or may not have that much impact on performance. However, if we have a huge application with, let’s say, an admin dashboard as well as a customer portal, it is not a good idea to try to load the complete application at initial loading times.

Why React Router?

React Router is a dynamic routing library for React. It is composed of the following packages: react-router, react-router-dom, and react-router-native. The core is the react-router, while the other two are environment specific. React-router-dom is for web apps, and react-router-native is for mobile apps using React Native.

The router manages the URLs whenever we need to navigate through our React app with multiple views. React Router keeps our app UI and URL in sync with each other. The benefits include the following:
  • It is compostable.

  • It is easy to add links to the web page.

  • It can add routing to several different pages on SPAs.

  • It conditionally renders components based on the route from the URL.

Why Material-UI?

One of the fundamental reasons for using a UI library is that it saves us time styling our app. We don’t need to reinvent the wheel, and we can just use something readily available, and more importantly, it’s already battle-tested by the community.

Choosing a UI design depends on your project specifications, of course. Still, usually, I’d look first at the most recommended and popular among other developers, like in the case of Material-UI. Again, I’d look at the active contributors on GitHub, contributors helping to maintain the project. In npmjs.com, Material-UI has about 1.4 million weekly downloads.

There’s also a higher chance that any issues you might encounter in your applications have already been resolved and documented on Stack Overflow or other sites.

Other top UI designs worth checking out include Ant Design and React-Bootstrap. We would talk more about styling React components in later chapters.

Getting Started

Let’s start building our React Router navigation for our application. First, let’s make a few edits to some default files, and let’s build it up again in a step-by-step process.

In your src/app/ folder, delete the following two directories: containers/HomePage and components/NotFoundPage. See Figure 5-1.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig1_HTML.jpg
Figure 5-1

Deleting folders: containers and components

It should break something in the app. So open the file src/app/index.tsx and update the file with the following line of code:
export function App() {
  return (
    <BrowserRouter>
      <Helmet
        titleTemplate="%s - React Boilerplate"
        defaultTitle="React Boilerplate"
      >
        <meta name="description" content="A React Boilerplate application" />
      </Helmet>
      <Switch>
        <Route exact path= '/' component={} />
      </Switch>
      <GlobalStyle />
    </BrowserRouter>
  );
}
Listing 5-1

Updating index.tsx

Next, under the app directory, create another folder named views. And under views, create another folder named pages. Lastly, within the pages directory, create a new file and call it Home.tsx. See Figure 5-2 for the folder structure.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig2_HTML.jpg
Figure 5-2

New folder structure

Open the file Home.tsx and type the snippet rafce (short for react arrow function export component) if you’re using VS Code. In WebStorm, the snippet is rsc (short for React stateless component without prop types and ES6 module system).

Figure 5-3 shows how to use code snippets in our code editor. Ensure you have already installed the extension ES7 React/Redux/GraphQL/React-Native snippets from the VS Code Marketplace.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig3_HTML.jpg
Figure 5-3

Typing the rafce snippet, short for react arrow function export component

Add the heading in the return statement <h1>Home Page</h1> as shown in Listing 5-2.
import React from 'react';
const Main = () => {
  return (
    <div>
      <h1>Home Page</h1>
    </div>
  );
};
export default Main;
Listing 5-2

Adding h1 Heading to the Home Page

In our app/index.tsx file , we add the Home component to our Route path, as shown in Listing 5-3.
export function App() {
  return (
    <BrowserRouter>
      <Helmet
        titleTemplate="%s - React Boilerplate"
        defaultTitle="React Boilerplate"
      >
        <meta name="description" content="A React Boilerplate application" />
      </Helmet>
      <Switch>
        <Route exact path="/" component={Home} />
      </Switch>
      <GlobalStyle />
    </BrowserRouter>
  );
}
Listing 5-3

Adding the Home Component to routes.tsx

When you run the app and go to your default localhost:3000, you should see the rendered page.

Figure 5-4 is the Home Page being rendered in the UI.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig4_HTML.jpg
Figure 5-4

Rendering the Home Page to the UI

What did we do here? We’ve shown a straightforward way of navigating our app using React Router.

Next, let’s create another file and name it material-buttons.tsx, and the path shall look like this: app/components/material-buttons.tsx.

Here we’ll render some buttons in the UI. We will use the design coming from Material-UI that we’ve imported earlier. Go to the website of Material-UI, specifically the Button component.

Figure 5-5 shows the link to the Material-UI website.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig5_HTML.jpg
Figure 5-5

Using the Material-UI Button component

Choose the Contained Buttons design. Make sure to choose the TypeScript option and copy the complete source code and paste it in our material-buttons.tsx file .

Important Note

Rename the file from ContainedButtons to MaterialButtons.

Afterward, go to the Home.tsx to use the newly created Material-UI Button component, as shown in Listing 5-4.
import React from "react";
import MaterialButtons from 'app/components/material-buttons’;
const Home = () => {
  return (
    <div>
          <h1>Main Page</h1>
          <MaterialButtons/>
    </div>
  );
};
export default Home;
Listing 5-4

Copying the Material-UI Button Component

In your localhost, you should see the following changes, as shown in Figure 5-6.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig6_HTML.jpg
Figure 5-6

Rendering the Material-UI Button component

Now that we have a proof of concept that we can use the Material-UI library in our design, we can look to configuring a router and then navigation.

But first, let’s quickly recap the React Router installation.

Basics Recap

With the installation of react-router-dom as part of our boilerplate, we gained access to the following three components in our index.tsx: <BrowserRouter>, <Route>, and <Switch>.

Open your index.tsx file, and let’s discuss each one:

<BrowserRouter> is the base configuration. It wraps the other components and keeps them in sync with the URL.

<Switch> is the dynamic part of the web application. As the name suggests, it will change or dynamically switch based on the URL. The Switch component makes sure that the first Route child component that matches the URL location will render.

<Route> needs a “path prop” and is rendered when it gets the matched or exact URL path.

Now let’s see how we navigate the URL paths in our application to get from one component to the next.

React-router-dom allows us a few ways of changing paths, including the most common one called the <Link> tag, which we would be using a lot here in our app. For a cleaner code and for separation of concerns, we shall create a new file in the app directory and name it routes.tsx.

Creating routes.tsx

Our Route path will likely become more extended, so instead of building all the routes in our app/index.tsx file, we will create a new file and name it routes.tsx .

Here’s the file path:
app ➤ routes.tsx
Next, we will then move the <Switch> and <Route> components from the index.tsx to our newly created routes.tsx, as shown in Listing 5-5.
import React, { lazy, Suspense } from 'react';
import { Switch, Route } from 'react-router-dom';
import Home from './views/pages/Home';
const Routes = () => {
  return (
      <Switch>
        <Route exact path="/" component={Home} />
       </Switch>
);
}
export default Routes;
Listing 5-5

Moving Switch and Route Components to routes.tsx

Now we need to use our newly created <Routes /> back at index.tsx, as shown in Listing 5-6.
export function App() {
  return (
    <BrowserRouter>
      <Helmet
        titleTemplate="%s - React Boilerplate"
        defaultTitle="React Boilerplate"
      >
        <meta name="description" content="A React Boilerplate application" />
      </Helmet>
      <Routes/>
      <GlobalStyle/>
    </BrowserRouter>
  );
}
Listing 5-6

Using the Routes in index.tsx

Let’s create another view page and name it AboutPage.tsx under the path views/pages. Add an <h1>This is the About Page</h1>.

Listing 5-7 is the About Page component.
import React from 'react';
const AboutPage = () => {
return (
  <div>
   <h1>This is the About Page</h1>
  </div>
);
 };
export default AboutPage;
Listing 5-7

Creating AboutPage and Adding h1 Heading

Don’t forget to add the new component in the routes.tsx as well as import the About Page component on top:
<Route exact path="/about" component={AboutPage} />

Check your localhost:3000/about to see if it’s still working.

Note

When building an application, I usually gather the requirements before writing any code. I always check whether I need to create a dashboard for the application or an admin dashboard. As much as possible, we make the routing first and determine if there are nested routes or not.

Now we can navigate the URL paths in our application. The first task is to build a dashboard.

Building a Dashboard

Go to the website of Material-UI and search for a navigation bar or app bar. They’re the same. Grab the TS source code of Simple App Bar. See a screenshot of Simple App Bar in Figure 5-7.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig7_HTML.jpg
Figure 5-7

Screenshot of Simple App Bar at the Material-UI website

Navigation Bar

The navbar component is what we place on top of the app and allows us to switch between different pages such as Home, About, Login, etc.

Create a new file under the folder components and name it navigation-bar.tsx.

Paste the source code that you grabbed from Material-UI Simple App Bar (see Figure 5-8). Don’t forget to change the default filename from ButtonAppBar to NavigationBar.

At this point, let’s just delete the file material-buttons.tsx since we don’t need it anymore. We just created it to quickly show how we can use the Material-UI designs in our component pages.

After that, open again the app/index.tsx and render the NavigationBar. Make sure to put it on top of or before the <Routes/> component to permanently position the navigation bar on the web app’s top area, as shown in Listing 5-8.
export function App() {
  return (
    <BrowserRouter>
      <Helmet
        titleTemplate="%s - React Boilerplate"
        defaultTitle="React Boilerplate"
      >
        <meta name="description" content="A React Boilerplate application" />
      </Helmet>
      <NavigationBar />
      <Routes/>
      <GlobalStyle/>
    </BrowserRouter>
  );
}
Listing 5-8

Using the NavigationBar in index.tsx

Check your localhost:3000 to see the navbar in the UI.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig8_HTML.jpg
Figure 5-8

Showing the NavigationBar at the browser

We will discuss in detail the various React styling methods in the following few chapters. But in the meantime, I just want to point out that the styling solution of Material-UI comes from many other styling libraries, such as styled-components.

At its core, Material-UI uses CSS-in-JS that works at runtime and server-side. We will talk more about CSS-in-JS in a later chapter.

Take a look at the navigation bar styling components from Material-UI. We are using makeStyles, which basically allows us to create multiple style rules per stylesheet. The returned function, useStyles, we will then call inside the NavigationBar component:
const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
    },
    menuButton: {
      marginRight: theme.spacing(2),
    },
    title: {
      flexGrow: 1,
    },
  }),
);

In my opinion, Material-UI is one of the best styling libraries, especially for React apps. The components are reusable, fast, and declarative. On its GitHub, it also has over 63K stars, which means I’m not alone, in my opinion.

Adding Navigation Links

Let’s add some buttons and navigation links in our navigation bar and name them as follows – <Home> <About> <Dashboard> – as shown in Listing 5-9.

Don’t forget to import the named component from “react-router-dom”:
import { Link } from 'react-router-dom';
Note

We will discuss in detail later in this chapter the different navigation paths we can use for our React app, but for now, let’s build our components first.

import React from 'react';
import { createStyles, makeStyles, Theme } from '@material-ui/core/styles';
import AppBar from '@material-ui/core/AppBar';
import Toolbar from '@material-ui/core/Toolbar';
import Button from '@material-ui/core/Button';
import { Link } from 'react-router-dom';
import { colors } from '@material-ui/core';
export default function NavigationBar() {
  const classes = useStyles();
  return (
    <div className={classes.root}>
      <AppBar position="static">
        <Toolbar>
          <Link className={`${classes.link} ${classes.title}`} to={'/'}>
            LOGO
          </Link>
          <Button color="inherit">
            <Link to={'/'}>
              Home
            </Link>
          </Button>
          <Button color="inherit">
              About
          </Button>
          <Button color="inherit">
              Dashboard
          </Button>
          <Button color="inherit">
              Login
          </Button>
        </Toolbar>
      </AppBar>
    </div>
  );
}
Listing 5-9

Adding Navigation Links to the navbar

While at it, replace the word “News” with LOGO because that’s where we’re going to put our icon logo later.

We also added the link style object. See the edits in Listing 5-10.
const useStyles = makeStyles((theme: Theme) =>
  createStyles({
    root: {
      flexGrow: 1,
    },
    menuButton: {
      marginRight: theme.spacing(2),
    },
    link: {
     color: colors.lightBlue[50],
     textDecoration: 'none',
     },
    title: {
      flexGrow: 1,
    },
  }),
);
Listing 5-10

Adding a link Style Object

As you can see in Listing 5-11, we can now use it in our button navigation links using the reserved React word <className>.

First, we used the useStyles Hook by calling it and storing it in a variable. We named the variable classes for readability purposes:
  const classes = useStyles();
And then let’s add the navigation links to the rest of the buttons.
export default function NavigationBar() {
  const classes = useStyles();
  return (
    <div className={classes.root}>
      <AppBar position="static">
        <Toolbar>
          <Link className={`${classes.link} ${classes.title}`} to={'/'}>
            LOGO
          </Link>
          <Button color="inherit">
            <Link className={classes.link} to={'/'}>
              Home
            </Link>
          </Button>
          <Button color="inherit">
            <Link className={classes.link} to={'/about'}>
              About
            </Link>
          </Button>
          <Button color="inherit">
            <Link className={classes.link} to={'/dashboard'}>
              Dashboard
            </Link>
          </Button>
          <Button color="inherit">
            <Link className={classes.link} to={'/login'}>
              Login
            </Link>
          </Button>
        </Toolbar>
      </AppBar>
    </div>
  );
}
Listing 5-11

Adding the Navigation Links and CSS Class Objects in the NavigationBar

In our LOGO component, notice that we need to use the backticks `` to use two CSS class objects in our Button component.
Link className={`${classes.link} ${classes.title}`} to={'/'}> LOGO </Link>
Listing 5-12

Using Backticks for Two or More CSS Class Objects

Make sure to clean up any unused or grayed-out imported libraries.

Next, let’s add another component under views/pages and name it NotFoundPage.tsx.

Type again the snippet “rafce” to conveniently create the stateless arrow component for us. And also add the heading <h1> 404 Page Not Found</h1>.
import React from 'react';
const NotFoundPage = () => {
  return (
      <div>
         <h1>404 Page Not Found</h1>
      </div>
      )
 }
export default NotFoudPage;
Listing 5-13

Creating a 404 Not Found Page

Don’t forget to define the path of NotFoundPage after importing it in routes.tsx:
import NotFoundPage from './views/pages/NotFoundPage';
<Route exact path="/not-found"component={NotFoundPage} />

Navigating React Router: <Redirect >

In our routes.tsx, let’s add <Redirect />, which is fortunately already built into the react-router-dom.

There are many use cases for redirecting our user to a particular page. In general, we use <Redirect /> if we need to change the URL path without the user having to click a link to that path.

Here are a few conditions:
  • When the user is accessing a restricted page.

  • When the user is accessing a page that is not found or no longer exists.

  • When the user typed an incorrect path into the address bar (typo error!).

  • A successful login has occurred, and the user is now being directed to the home page or dashboard.

For now, let’s create a <Redirect /> for all paths that are not defined on the 404 page:
<Route path={'/not-found'} component={NotFoundPage} exact />
        <Redirect from={'*'} to={'/not-found'} exact />

Before we wrap up this section, let’s just put a bit of styling on our pages, especially paddings and margins.

Adding the Container-Style Class Component

Go to app/index.tsx, and we’re going to wrap our <Routes/> in a Container using Material-UI. Import the named component from Material-UI and directly use the <Container> to wrap the Routes component:
...
import { Container } from '@material-ui/core';
<Container>
    <Routes />
</Container>
Check the browser, and you’ll notice, as shown in Figure 5-9, that the <h1> headings are no longer pushed out into the left corner. The styling applies to all the pages that were wrapped in <Container> <Routes/> </Container>.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig9_HTML.jpg
Figure 5-9

Using Container from Material-UI

But don’t worry too much about the specific styling for these pages. We’re just showing you the easy convenience of using Material-UI to style our app.

Remember that this book’s primary goals are to guide you to build an enterprise-level app and give you an idea of how developers develop their apps similarly. In the end, you still decide on how you will make your app based on your specific requirements.

So what about the dashboard? Let’s build that up now.

Creating the Dashboard Layout

In the app folder, create a folder named layouts, and after which, make a subfolder and call it dashboard-layout.

In the dashboard-layout folder, create the file dashboard-sidebar-navigation.tsx.

The path is applayoutsdashboard-layoutdashboard-sidebar-navigation.tsx.

Let’s populate the newly created file with the following code. First, import the following libraries.
import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';
import { createStyles, makeStyles } from '@material-ui/core/styles';
import Drawer from '@material-ui/core/Drawer';
import Toolbar from '@material-ui/core/Toolbar';
import { useRouteMatch } from 'react-router';
Listing 5-14

Importing Libraries to dashboard-sidebar-navigation.tsx

Okay, let’s briefly explain a couple of the unfamiliar libraries we imported here before we proceed. We will start with the long-overdue <Link> from react-router-dom and the other ways of navigating paths in our app.

Navigating Path: <Link>

The <Link> from react-router-dom allows users to navigate the application and have pages re-rendered without a refresh. It allows for a declarative and easy navigation path around our app.

Some common ways we can use it:
  • to: string: Created by concatenating the location’s pathname, search, and hash properties. For example: <Link to="/products/?sort=name" />.

  • to: object: This can be any of the following props: pathname, search, hash, and state.

  • replace: bool: When true, clicking the link replaces the current entry in the history stack instead of adding a new one. For example: <Link to="/products" replace />.

  • to: function: The current location is passed as an argument, and location representation should be returned as an object or string. For example:

<Link to={location => ({ ...location, pathname: "/products" })} />
<Link to={location => `${location.pathname}?sort=name`} />

React Hook: useRouteMatch

useRouteMatch is a Hook that we typically need when we are using <Route>. useRouteMatch allows us to access to a <match> object and use it inside the component; so instead of rendering a <Route>, just use useRouteMatch.

Okay, now that we’ve discussed some of the libraries we’ve imported here, let’s now create our DashboardSidebarNavigation component.
const DashboardSidebarNavigation = () => {
  const classes = useStyles();
  const { url } = useRouteMatch();
  useEffect(() => {}, []);
  return (
      <div className={classes.root}>
        <Drawer
          className={classes.drawer}
          variant="permanent"
          classes={{
            paper: classes.drawerPaper,
          }}
          anchor="left"
        >
          <Toolbar
            style={{ width: '6rem', height: 'auto' }}
            className={classes.toolbar}
          >
            <Link to={`${url}`} className={classes.logoWithLink}>
              Logo
            </Link>
          </Toolbar>
        </Drawer>
      </div>
  );
};
export default DashboardSidebarNavigation;
Listing 5-15

Creating the DashboardSidebarNavigation Component

We’re using styling from Material-UI Drawer and Toolbar and using the <Link> component as our navigation path.

The styling components are coming from Material-UI.
const drawerWidth = 240;
const useStyles = makeStyles(theme =>
  createStyles({
    root: {
      display: 'flex',
    },
    drawer: {
      width: drawerWidth,
      flexShrink: 0,
    },
    drawerPaper: {
      width: drawerWidth,
    },
    drawerContainer: {
      overflow: 'auto',
    },
    toolbar: theme.mixins.toolbar,
    content: {
      flexGrow: 1,
      padding: theme.spacing(3),
    },
    link: { textDecoration: 'none', color: 'inherit' },
    logoWithLink: {
      display: 'flex',
      alignItems: 'center',
      textDecoration: 'none',
      color: 'inherit',
    },
  }),
);
Listing 5-16

Styling the dashboard-sidebar-navigation Using Material-UI

After finishing the dashboard-sidebar-navigation.tsx, create an index.tsx under the dashboard-layout folder and copy the following code.
import React from 'react';
import { Grid } from '@material-ui/core';
import DashboardSidebarNavigation from './dashboard-sidebar-navigation';
type Props = {
  children: React.ReactNode;
};
const Dashboard = ({ children }: Props) => {
  return (
    <Grid
      container
      direction="row"
      justify="flex-start"
      alignItems="flex-start"
    >
      <DashboardSidebarNavigation /> {children}
    </Grid>
  );
};
export default Dashboard;
Listing 5-17

Creating the Dashboard Component

Dashboard Component

So what’s going on here? We have the main Dashboard component and its children props, which are the main content pages. The children props have routers for users to navigate successfully from one view or page to another.

Bear in mind that the children parameter in the Dashboard is the entry point for React components. For example, if we pass a Button component in the Dashboard, it will be rendered beside the DashboardSidebarNavigation component.

We now have a reusable layout of the Dashboard that contains a DashboardSidebarNavigation.

As for the <Grid>, it is used for the positioning of the layouts. The grid’s responsive layout, which is based on a 12-column grid layout, is quite powerful because it can adapt to any screen size and orientation.

Now that we already have the layout, the next step is to create a dashboard default component, which is the first thing that users will see in the Dashboard.

In views, create a new dashboard folder, and inside that create two new files: dashboard-default-content.tsx and settings-and-privacy.tsx.

The path looks like this:
 Views ➤ dashboard ➤ dashboard-default-content.tsx
 Views ➤ dashboard ➤ settings-and-privacy.tsx

Type in the snippet “rafce” to create a stateless arrow component and return an <h1> with the title heading the same as the component’s name. See the following sample.

Also, make sure to rename and use the Pascal naming convention in the component’s name.
import React from 'react';
const DashboardDefaultContent = () => {
  return (
    <div>
      <h1>Dashboard Default Content</h1>
    </div>
  );
};
export default DashboardDefaultContent;
Listing 5-18

Creating the DashboardDefaultContent Component

Do the same thing to the settings-and-privacy.tsx file.
import React from 'react';
const SettingsAndPrivacy = () => {
  return (
    <div>
      <h1>Settings and Privacy</h1>
    </div>
  );
};
export default SettingsAndPrivacy;
Listing 5-19

Creating the SettingsAndPrivacy Component

For now, we’re just setting up the Dashboard and navigation without the styling because we just want to show a proof of concept that we’re able to navigate successfully from one page to the next.

Now, let’s define the path in our routes.tsx. But in this case, we will be using render props.

Render Props

On the website www.reactjs.org, the term render props refers to “a technique for sharing code between React components using a prop whose value is a function.”

Okay, simply, it means that our Route component has a render prop that takes a function as a value. The Route component then uses that function to render whatever the function provides – in our case, the Dashboard.

We’re just reusing code, actually, in an efficient way.
<Route path={'/dashboard'}
                render={({match: {path}}) => (
                    <Dashboard>
                        <Switch>
                            <Route exact path={path + '/'}
                                component={DashboardDefaultContent}/>
   <Route exact path={path + '/settings-and-privacy'}
                component={SettingsAndPrivacy}/>
                        </Switch>
                    </Dashboard>
                   )}>
 </Route>
Listing 5-20

Using the Render Props in routes.tsx

In the React Router library, the <Route /> carries the behavior of navigating to different views or pages. It renders the component when the path matches.

Next, let’s update the dashboard-sidebar-navigation.tsx to add the settings and privacy button and a logout button.

For a bit of styling, first, let’s import the components, as shown in Listing 5-21, from Material-UI to our dashboard-sidebar-navigation.tsx.
import { List, ListItem, ListItemIcon, ListItemText } from '@material-ui/core';
import ExitToAppIcon from '@material-ui/icons/ExitToApp';
import SettingsIcon from '@material-ui/icons/Settings';
Listing 5-21

Importing the Material-UI Components to dashboard-sidebar-navigation.tsx

The additional styling components for the dashboard sidebar navigation are as shown in Listing 5-22.

Right after the </Toolbar> and still within the <Drawer />, add the following code:
<return (
      <div className={classes.root}>
        <Drawer
          className={classes.drawer}
          variant="permanent"
          classes={{
            paper: classes.drawerPaper,
          }}
          anchor="left"
        >
          <Toolbar
            style={{ width: '6rem', height: 'auto' }}
            className={classes.toolbar}
          >
            <Link to={`${url}`} className={classes.logoWithLink}>
              Logo
            </Link>
          </Toolbar>
          <div className={classes.drawerContainer}>
            <List>
              <Link className={classes.link} to={`${url}/settings-and-privacy`}>
                <ListItem button>
                  <ListItemIcon>
                    <SettingsIcon />
                  </ListItemIcon>
                  <ListItemText primary={'settings and privacy'} />
                </ListItem>
              </Link>
              <a className={classes.link} href={'/'}>
                <ListItem button>
                  <ListItemIcon>
                    <ExitToAppIcon />
                  </ListItemIcon>
                  <ListItemText primary={'logout'} />
                </ListItem>
              </a>
            </List>
          </div>
        </Drawer>
      </div>
  );
};
Listing 5-22

Additional Components and Styling for dashboard-sidebar-navigation.tsx

If you click the Dashboard button in our UI, you should be able to see the following additional components:

settings and privacy

logout

../images/506956_1_En_5_Chapter/506956_1_En_5_Fig10_HTML.jpg
Figure 5-10

Dashboard Default Content UI

If you click settings and privacy, you should successfully navigate to that page. The same goes for when you click Home, About, or Login – the last one is currently just a 404 page because we haven’t built that one yet. Also, logout, for now, is just basically refreshing the page.

Going into React.lazy( )

But wait. We’re not done yet. We can still improve our route navigation. And we can do this with the help of lazy loading, which is one of the most effective ways to speed up our bundles’ loading times.

So what is lazy loading?

The concept of lazy loading is easy to understand. It is merely rendering first what is essential to the user interface while quietly loading nonessential sections or items as needed.

Lazy loading in React is a relatively new feature that was released in version 16.6.0. It has come about as applications have become more complex, powerful, and massive, leading to increased loading times and negatively impacting the user experience.

And as a best practice, we should always need to consider users, including those using mobile data and slow Internet connection.

Note

React.lazy(), which is now fully integrated into the core React library, has replaced a third-party library react-loadable.

But use react-loadable for server-side code-splitting since React.lazy () and Suspense are not yet available for server-side rendering.

React lazy loading allows us to load components slowly or gradually using code-splitting without installing additional libraries. This approach enables us to render the necessary or only critical interface items at initial loading times while lazily and quietly unrolling the other sections as needed.

According to the official React docs, the best way to do code-splitting in our React app is through the dynamic import () syntax. Eager loading is the default behavior of our web app, meaning if you download all the page resources, for example, the Home Page, then everything will be downloaded in one go.

The potential problem with this is that it can create a bottleneck if the application is huge – hence, we need to do code-splitting.

Let’s show some sample codes before and after code-splitting.
import MyComponent from './MyComponent';
function OneComponent() {
     return (
          <div>
               <MyComponent />
          </div>
     );
}
Listing 5-23

Showing Eager Loading vs. Lazy Loading

const MyComponent = React.lazy(() => import('./MyComponent'));
function OneComponent() {
     return (
           <div>
                <MyComponent />
           </div>
     );
}

Note that components created using React.lazy() need to be wrapped in React.Suspense, so let’s review that now.

React Suspense

Suspense acts like placeholder content while the lazy components are loaded. Note that we can wrap several lazy components at various hierarchy levels with just a single Suspense component.

A little caveat: In the official React docs, Suspense is still an experimental API and could still undergo some changes until such time when the React team says it’s fully complete for production-ready work.

However, this notice, which has been on for a couple of years, has not deterred React developers from using React.lazy() and Suspense in big applications or production-level apps.

Tip: For the best user experience, wrap your lazy component in an error boundary <ErrorBoundary /> if it fails to load.

Let’s take a look at the DevTools and show the eager loading of the Home Page and About Page. Everything is loaded at one go, as shown in Figure 5-11.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig11_HTML.jpg
Figure 5-11

A screenshot of eager loading of the Home Page and About Page in DevTools

You’ll see that ALL components are loaded even at the app’s initial loading. This means that even if you navigate from the Home Page to the About Page, nothing new is added.

So now, let’s see the difference between a component that is eager loading and one that is lazy loading.

Eager Loading vs. Lazy Loading

Now, let’s compare a component that is wrapped in lazy loading against an eager loading component.

In our routes.tsx, import the named components:
import React, {lazy, Suspense}from 'react';

But before we can compare the two components, we need to wrap our lazy component with React Suspense. So let’s do that now.

Next, for our comparison, we will put on lazy load the following components: About and Dashboard.
const Routes = () => {
  return (
    <Suspense>
    <Switch>
      {/*eager loading */}
      <Route exact path="/" component={Home} />
      {/*lazy loading */}
      <Route exact path="/about" component={AboutPage} />
      {/*lazy loadings*/}
        <Route
         exact path={'/about'}
          component={lazy(() => import('./views/pages/AboutPage'))}
           />
        <Route
           exact path={'/dashboard'}
          render={({ match: { path } }) => (
            <Dashboard>
              <Switch>
                <Route
                  path={path + '/'}
                  component={lazy(
                    () => import('./views/dashboard/dashboard-default-content'), )}
                />
              <Route
                exact
                path={path + '/settings-and-privacy'}
                component={SettingsAndPrivacy}
              />
            </Switch>
          </Dashboard>
        )}
      ></Route>
      <Route exact path="/not-found" component={NotFoundPage} />
      <Redirect from={'*'} to={'/not-found'} exact />
    </Switch>
    </Suspense>
  );
};
Listing 5-24

Lazy Loading the Components About and Dashboard for Comparison

BUT wait. If you look at the browser, you’ll see the following error.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig12_HTML.jpg
Figure 5-12

Failed to Compile React Suspense

This is because Suspense requires the property “fallback.”

React looks up the tree, encounters the first <Suspense> component, and renders its fallback.

Use the required Suspense prop called fallback and don’t forget to import the named component too. The LinearProgress bar is from Material-UI.
...
import { LinearProgress } from '@material-ui/core';
export const Routes = () => {
  return (
    <Suspense fallback={<LinearProgress style={{ margin: '10rem' }} />}>
Listing 5-25

Using the Suspense Prop fallback

Okay, that should make our Suspense component working now. You can also lazy load the settings and privacy, but make sure to leave the Home Page on eager loading so we can see the comparison.

Refresh the browser. Open the DevTools while refreshing the Home Page, as shown in Figure 5-13.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig13_HTML.jpg
Figure 5-13

Eager loading of the Home Page at initial loading time

But when you click the lazy-loaded About Page, you can see that the highlighted portions are just the ones added. The highlighted parts were not initially rendered on the Home Page because they were not needed yet. See Figure 5-14.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig14_HTML.jpg
Figure 5-14

Lazy loading the About Page

You’ll notice the same when you click the lazy-loaded Dashboard. See Figure 5-15.
../images/506956_1_En_5_Chapter/506956_1_En_5_Fig15_HTML.jpg
Figure 5-15

Lazy loading the Dashboard

The highlighted portion was the only thing loaded when we navigated from the About Page to the Dashboard page.

You might think that the file and time to load may be insignificant or small, but imagine if your app already got massive lines of codes and you’ve got 50 pages or more. Your user, especially on a mobile app or a slow Internet connection, would feel it.

Summary

I hope by now you have a fair idea of how client-side routing works in general and how to implement routing in React using the React Router library. We’ve also touched on how to use lazy loading in our app to turbo-charge our initial loading times with the end goal of improving user experience.

In the next chapter, we will tackle writing local states, sending HTTP requests, and using ApexCharts.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
52.14.150.55