React-router

From bibbleWiki
Jump to navigation Jump to search

Introduction

This page is about routering with React.

Setup

Install package for Router

npm i react-router-dom

Add BrowserRouter to Index.js

...
import { BrowserRouter } from 'react-router-dom';
...
ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);

Add Routes to App.js

We need to a routes to the the router we support.

import { BrowserRouter as Router, Switch, Route, Link } from 'react-router-dom';

import Explore from './components/Explore';
import Home from './components/Home';
import Messages from './components/Messages';
import Profile from './components/Profile';

function App() {
  return (

    <Router>
        <div>
          <h2>Welcome to React Router Tutorial</h2>
          <nav className="navbar navbar-expand-lg navbar-light bg-light">
          <ul className="navbar-nav mr-auto">
            <li><Link to={'/'} className="nav-link"> Home </Link></li>
            <li><Link to={'/explore'} className="nav-link">Explore</Link></li>
            <li><Link to={'/messages'} className="nav-link">Messages</Link></li>
            <li><Link to={'/profile'} className="nav-link">Profile</Link></li>
          </ul>
          </nav>
          <hr />
          <Switch>
              <Route exact path='/' component={Home} />
              <Route path='/explore' component={Explore} />
              <Route path='/messages' component={Messages} />
              <Route path='/profile' component={Profile} />
          </Switch>
        </div>
      </Router>

  );
}

export default App;

Router Children Component

All router children components get the following props.

  • match Exact Match, Params, Path and matching Url
  • location Key, path name and search
  • history This is the browser history

NavLink Component

This allows us to work with the activeClassName and exact to highlight the list which is active.

  <Router>
    <Nav authenticated={!defaultToSignIn} />
    <hr />
    <Switch>
      <Route path="/" component={Home} exact />
      <Route path="/auth/login" component={Login} />
      <Route path="/auth/Register" component={Register} />
      <Route path="/auth/logout" component={Logout} />
    </Switch>
  </Router>

And for some of the NavLink routes

            <li className="nav-item">
              <NavLink
                to="/home"
                aria-current="page"
                className="nav-link"
                exact
              >
                Home
              </NavLink>
            </li>
        </ul>
        <ul className="navbar-nav ms-auto mb-2 mb-lg-0">
            <li className="nav-item">
              <NavLink to="/explore" aria-current="page" className="nav-link">
                Explore
              </NavLink>
            </li>
            <li className="nav-item">
              <NavLink to="/messages" aria-current="page" className="nav-link">
                Messages
              </NavLink>
            </li>

Prompt Component

We can prevent leaving of a page with the Prompt component. For example.

...
  return (
    <div className="Login">
      {error && <p intent="danger">{error}</p>}
      <Prompt
        when={!usernameValid || !passwordValid}
        message="Leaving page will loose your data"
      />
      <Form onSubmit={handleSubmit} className="row g-3 needs-validation">
        <div>
          <div className="col-md-4">
            <Form.Group size="lg" controlId="username">
              <Form.Label>Username</Form.Label>
              <Form.Control
...

Adding a 404 Page

To implement this we just create a route with no path and a component

  <Router>
    <Nav authenticated={!defaultToSignIn} />
    <hr />
    <Switch>
      <Route
        exact
        path="/"
        render={() =>
          defaultToSignIn ? (
            <Redirect to="/auth/login" />
          ) : (
            <Redirect to="/home" />
          )
        }
      />
      <ProtectedRoute path="/" component={Home} exact />
      <ProtectedRoute path="/explore" component={Explore} />
      <ProtectedRoute path="/messages" component={Messages} />
      <ProtectedRoute path="/profile" component={Profile} />
      <Route path="/auth/login" component={Login} />
      <Route path="/auth/Register" component={Register} />
      <Route path="/auth/logout" component={Logout} />
      <Route component={PageNotFound} />
    </Switch>
  </Router>

Rendering Component with Parameters

When you need to add props to the component to render we use the render property which takes a function as an argument and should return the Component for the page.

Router

  <Router>
    <Switch>
      <Route path="/auth/logout" component={Logout} />
      <Route path="/compwithprops" render={() => {
           return <CompWithProps prop1="#ff0000" prop2='Red'/>
      }) />
      <Route component={PageNotFound} />
    </Switch>

Component

For the component we need to wrap it with withRouter in a HOC.

import React from "react";

const CompWithProps = ({prop1,prop2}) => (
  <div>
..
  </div>
);

CompWithProps.proptypes = {
    prop1: PropTypes.string.isRequired,
    prop2: PropTypes.string.isRequired,
}

export default withRouter(CompWithProps);

Custom Route with Parameters

Here we create a custom route which takes a parameter :eid

...
<CustomRoute path={'/api/orders/:eid'} component={Home} />
...

I the custom route we now Here we can create a Route which pulls this data in the props. Like the example above we can render the component Home and the eid will be available in the props.match.params.eid

import React from "react";
import { Route } from "react-router-dom";

const CustomRoute = ({ component: ComponentToRender, ...rest }) => (
  <Route
    {...rest}
    render={(props) => {
      alert(`Cusom Route ${props.match.params.eid} `);
      return <ComponentToRender {...props} />;
    }}
  />
);
export default CustomRoute;

Protected Route

Authentication Service

We create an authService which emulates the following

import Cookies from "universal-cookies";

const cookies = new Cookies();
const authService = {
  isAuthenticated() {
    return cookies.get("auth") === "true";
  },

  signIn(cb) {
    cookies.set("auth", true, { path: "/" });
    setTimeout(cb, 100);
  },

  signOut(cb) {
    cookies.set("auth", false, { path: "/" });
    setTimeout(cb, 100);
  },
};

export default authService;

Create Private Route

We create this function which checks if the user is authenticated and either renders the Component passed in or redirects to the login page (commented out). This is great for a new user but if we click a bookmark our expectation is that we would be redirected to login, enter credentials and then redirect back to the original request. This is achieved with the highlighted code.

import React from "react";
import { Redirect } from "react-router-dom";
import authService from "../service/authService";

const PrivateRoute = ({ component: Component, ...rest }) => (
  <Route {...rest}
    render={(props) => {
      authService.isAuthenticated() ? (
        <Component {...props} />
      ) : (
//        <Redirect to="/login" />
          <Redirect to={
              pathname: '/login',
              state: { target: props.location }    
          } />
      );
    }}
  />
);

export default PrivateRoute;

Change Routes to Use Private Route

Now we put the private where appropriate

  <Router>
      <PrivateRoute path="/" component={Home} exact />
      <PrivateRoute path="/explore" component={Explore} />
      <Privateoute path="/messages" component={Messages} />
      <PrivateRoute path="/profile" component={Profile} />
      <Route path="/auth/login" component={Login} />
      <Route path="/auth/Register" component={Register} />
      <Route path="/auth/logout" component={Logout} />
      <Route component={PageNotFound} />
    </Switch>
  </Router>

Logout

Here is the example of the logout function.

import { Redirect } from "react-router-dom";
import authService from "../service/authService";

class Logout extends React.Component {
  constructor(props) {
    super(props);
  }

  componentWillMount = () => {
    authService.signOut(() => {});
  };

  render = () => {
    return <Redirect to={"/login"} />;
  };
}

export default Logout;

Transitions

You need to provide a class prefix in this case trans.

import {TransitionGroup, CSSTransition} from 'react-transition-group'

<TransactionGroup>
  <CSSTransition key={location.key} classNames={'trans'} timeout={1000}>
    <Switch location={location}>
     <Route path={`${match.url}`} component={LoremNumber} exact/>
     <Route path={`${match.url}/:id`} component={LoremNumber} exact/>
    </Switch>
</TransactionGroup>

And the CSS

.trans-enter {
   opacity: 0;
   z-index: 1;
}

.trans-exit {
   display: none
}

.trans-enter.trans-enter-active {
   opacity: 1;
   transition: opacity 1000ms ease-in;
}