Protected routes in react with react router and redux
Tags: react, redux, routes, reactrouter
Published: 2021-10-02 06:11:16

Protected routes can only be accessed by authenticated users in an application. React-router and redux have been winning a combination for a lot of SPA(single page applications), but for a newbie, figuring how to combine these two packages to implement a protected route can seem a bit complex. We'll be looking at how to implement protected routes with react-router and redux in a simplified manner.

We'll assume you are familiar with react. If however, you're unfamiliar with react you can checkout [https://reactjs.org/docs/getting-started.html](https://reactjs.org/docs/getting-started.html).

####SETUP

We'll start off by spinning up a react app with CRA(create-react-app). To learn more about CRA checkout [https://reactjs.org/docs/create-a-new-react-app.html](https://reactjs.org/docs/create-a-new-react-app.html).
```
npx create-react-app my-protected-app
```
####Dependencies
Redux is an open source library for managing state in centralised manner, it is very popular in the frontend community and a must for many dev roles. 

React router provides declarative routing for react. It is the go to library for react SPAs routing.

Install these dependencies to get going started
```
yarn add react-router-dom redux react-redux
or 
npm install react-router-dom redux react-redux --save
```
####Setting up our app
> NB: you can skip this part if your app is already setup

First we'll create a `Home` component.
```
import React from "react";

const Home = () => {
  return (
    <div className="App">
      <h1>Welcome to my protected route!</h1>
      <h2>We've got cookies</h2>
    </div>
  );
};

export default Home;
```
Home view on browser
![Home page view](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/o7hgv4cpe7kwff6d8gh1.png)

Then we'll create a `Login` component from which the user logs in to access the home page.
```
import React from "react";

const Login = () => {

  return (
    <div className="App">
      <div className="login-form">
        <h4 className="form-title">Login</h4>
        <div className="form-control">
          <input type="text" name="username" placeholder="Username" />
        </div>
        <div className="form-control">
          <input type="password" placeholder="Enter password" name="password" />
        </div>
        <button className="login-btn">
          Login
        </button>
      </div>
    </div>
  );
};

export default Login;
``` 
Login view
![Login view](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/pc3lii4hhwrocpd82ltj.png)

Then we add the `style.css` for the app styling.
```
html {
  box-sizing: border-box;
}
*,
*:before,
*:after {
  box-sizing: inherit;
}

.App {
  font-family: sans-serif;
  text-align: center;
  height: 100vh;
  width: 100%;
}

.login-form {
  width: 450px;
  padding: 20px 25px;
  border-radius: 10px;
  border: solid 1px #f9f9f9;
  text-align: left;
  margin: auto;
  margin-top: 50px;
  background: #b88bf72f;
}

.form-title {
  margin-top: 0;
  margin-bottom: 15px;
  text-align: center;
}

.form-control {
  margin-bottom: 15px;
  width: 100%;
}

.form-control input {
  border-radius: 5px;
  height: 40px;
  width: 100%;
  padding: 2px 10px;
  border: none;
}

.login-btn {
  padding: 5px 10px;
  border-radius: 5px;
  border: none;
  background: rgb(60, 173, 239);
  color: #fff;
}

``` 

####Setting up redux
Let's create `store` directory, then a `types.js` file in `src/store` to export our different store action types
```
export const LOGIN_USER = "LOGIN USER";
```
Next, we'll create a `store.js` file in our `src/store` folder. Here we instantiate our store and it's initial state.
```
import { createStore } from "redux";
import { LOGIN_USER } from "./types";

const intitialState = {
  authenticated: false
};

const reducer = (state = intitialState, action) => {
  switch (action.type) {
    case LOGIN_USER:
      return { ...state, authenticated: true };

    default:
      return state;
  }
};

const store = createStore(reducer);

export default store;
```
Our initial state object contains an authenticated state which is false by default, indicating a user is not logged in. We see more on changing this state. Checkout [createStore](https://redux.js.org/api/createstore) to learn more about setting up redux.

####Setting up react-router
In the `src/index.js` file
```
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter as Router } from "react-router-dom";
import App from "./App";

ReactDOM.render(
  <React.StrictMode>
    <Router>
      <App />
    </Router>
  </React.StrictMode>,
  document.getElementById("root")
);

```
We import `BrowserRouter` and wrap our `App` component in it.

At this point we create our `ProtectedRoute` to handle verifying a user is authenticated before rendering the component. If the user is not authenticated we want to redirect them to the login page. 
```
import React from "react";
import { useSelector } from "react-redux";
import { Redirect, Route } from "react-router-dom";

const ProtectedRoute = ({ path, exact, children }) => {
  const auth = useSelector((store) => store.authenticated);

  return auth ? (
    <Route path={path} exact={exact}>
      {children}
    </Route>
  ) : (
    <Redirect to="/login" />
  );
};

export default ProtectedRoute;
```
We check the authenticated state in our redux store and render the component on the condition that it authenticated is `true`.

Next, in our `App.js` we add `Switch` giving our app ability to switch components between routes. We also bring in our components, our protected route and set up our store in our App component.
```
import React from "react";
import "./styles.css";
import { Provider } from "react-redux";
import store from "./store";
import { Route, Switch } from "react-router-dom";
import ProtectedRoute from "./ProtectedRoute";
import Home from "./Home";
import Login from "./Login";

const App = () => {
  return (
    <Provider store={store}>
      <Switch>
        <Route path="/login">
          <Login />
        </Route>
        <ProtectedRoute exact path="/">
          <Home />
        </ProtectedRoute>
      </Switch>
    </Provider>
  );
};
export default App;
```
####Finishing up
To finish things up, we modify our `Login` component with the ability to change the state at login.
```
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { useHistory } from "react-router";

import { LOGIN_USER } from "./store/types";

const Login = () => {
  const dispatch = useDispatch();
  const history = useHistory();
  const [inputs, setInput] = useState({ username: "", password: "" });

  function inputChange(e) {
    setInput({ ...inputs, [e.target.name]: e.target.value });
  }

  function login(event) {
    event.preventDefault();
    if (!inputs.username || !inputs.password) return;
    dispatch({ type: LOGIN_USER });
    history.push("/");
  }

  return (
    <div className="App">
      <form onSubmit={login} className="login-form">
        <h4 className="form-title">Login</h4>
        <div className="form-control">
          <input
            type="text"
            name="username"
            placeholder="Username"
            onChange={inputChange}
          />
        </div>
        <div className="form-control">
          <input
            type="password"
            placeholder="Enter password"
            name="password"
            onChange={inputChange}
          />
        </div>
        <button type="submit" className="login-btn">
          Login
        </button>
      </form>
    </div>
  );
};

export default Login;
```
We use the `useDispatch` hook to dispatch actions to our redux store. Notice we use the `LOGIN_USER` type we create in our `store/types` in dispatch. We finally round by routing to the home route with the `useHistory` from react-router. Now as far as our inputs aren't empty we can login to the home page.

From here, more can be done to add extra features, congrats on your protected route.