Author avatar

Gaurav Singhal

How to Transition to Another Route on Successful Async Redux Action

Gaurav Singhal

  • Feb 3, 2020
  • 12 Min read
  • 39,491 Views
  • Feb 3, 2020
  • 12 Min read
  • 39,491 Views
Web Development
React

Introduction

Redirection in a single-page application can be a bit tricky, especially with React and Redux. We can redirect programmatically in the component itself or a middleware. In this guide, we are going to learn how to redirect when there's a successful async action.

There are several ways to redirect a user to another route, including the history.push() method and the <Redirect /> component from react-router. In this guide, we will use a Redux action to redirect the user using the <Redirect /> component.

We will dispatch an action to redirect a user to the home page after they submit a registration form.

Action Creators and Reducer

Let's begin with action creators and a reducer.

ui.js

In the ui.js file, we will create the REDIRECT action, and in its payload, we will pass the link of the redirect page. I have included a console.log() statement so that it will be more evident when the action gets dispatched.

1export const REDIRECT = "REDIRECT";
2
3// action creators
4export const redirect = link => {
5  console.log("=== REDIRECT ACTION DISPATCHED ===");
6  return { type: REDIRECT, payload: link };
7};
js

register.js

In the register.js file, we will create a REGISTER action, which will be dispatched when the user clicks on the Submit button of the registration form and will send the user data in the payload.

1export const REGISTER = "REGISTER";
2
3export const register = user => {
4  console.log("=== REGISTER ACTION DISPATCHED ===");
5  return {
6    type: REGISTER,
7    payload: user
8  };
9};
js

api.js

All the actions related to network requests like API_REQUEST, API_SUCCESS, and API_ERROR will be included in the api.js file.

1// action types
2export const API_REQUEST = "API_REQUEST";
3export const API_SUCCESS = "API_SUCCESS";
4export const API_ERROR = "API_ERROR";
5export const CANCEL_API_REQUEST = "CANCEL_API_REQUEST";
6
7// action creators
8export const apiRequest = ({ url, method, data }) => {
9  return {
10    type: API_REQUEST,
11    meta: { url, method, data }
12  };
13};
14
15export const cancelApiRequest = () => {
16  return {
17    type: CANCEL_API_REQUEST
18  };
19};
20
21export const apiSuccess = ({ response }) => ({
22  type: API_SUCCESS,
23  payload: response
24});
25
26export const apiError = ({ error }) => ({
27  type: API_ERROR,
28  payload: error
29});
js

reducer.js

The reducer will be reasonably simple; we will store the redirect link in the store. We don't need to add the user's data here, as it's not going to be used in any component of our application.

1const reducer = (state = {}, action) => {
2  switch (action.type) {
3    case REDIRECT:
4      return { redirectTo: action.payload };
5    default:
6      return state;
7  }
8};
9
10export default reducer;
js

In the next section, we will start writing the registration form.

The Registration Form

In the registration form, we will have three fields: name, email, and password. To make the UI beautiful and clean, I'll be using material-ui, but you can use any UI library of your choice.

1// ..
2<form>
3  <Typography variant="h5" style={{ marginBottom: 8 }}>
4    Create an account
5  </Typography>
6  <TextField
7    label="Name"
8    variant="outlined"
9    fullWidth
10    className="form-input"
11    value={name}
12    onChange={e => setName(e.target.value)}
13  />
14  <TextField
15    label="Email"
16    variant="outlined"
17    fullWidth
18    className="form-input"
19    value={email}
20    onChange={e => setEmail(e.target.value)}
21  />
22  <TextField
23    label="Password"
24    variant="outlined"
25    fullWidth
26    className="form-input"
27    type="password"
28    value={password}
29    onChange={e => setPassword(e.target.value)}
30  />
31  <Button
32    variant="contained"
33    color="primary"
34    fullWidth
35    className="form-input"
36    size="large"
37    onClick={submitForm}
38  >
39    Register
40  </Button>
41
42  {(props.error || error) && (
43    <Alert severity="error" onClick={() => setError(null)}>
44      {props.error || error}
45    </Alert>
46  )}
47</form>
48// ..
jsx

We'll create the form as a functional component and manage the state using the useState hook.

1const [email, setEmail] = useState("");
2const [password, setPassword] = useState("");
3const [name, setName] = useState("");
4const [error, setError] = useState("");
jsx

On Submit button click, we will do basic validations and dispatch the REGISTER action.

1const submitForm = () => {
2  if (email === "" || password === "" || name === "") {
3    setError("Fields are required");
4    return;
5  }
6  props.register({ name, email, password });
7};
jsx

From the registration page itself, we will redirect the user to the home page. To do that, we will check if we have the redirectTo property in the global state, and based on that redirect, the user using the <Redirect /> component from react-router.

1if (props.redirectTo) {
2  return <Redirect to={props.redirectTo} />;
3}
jsx

So finally, our registration page will be as follows :

1import React, { useState } from "react";
2import { TextField, Typography, Button } from "@material-ui/core";
3import { connect } from "react-redux";
4import { register } from "../actions/register";
5import MuiAlert from "@material-ui/lab/Alert";
6import { Redirect } from "react-router";
7
8function Alert(props) {
9  return <MuiAlert elevation={6} variant="filled" {...props} />;
10}
11
12export default connect(({ redirectTo }) => ({ redirectTo }), { register })(
13  props => {
14    const [email, setEmail] = useState("");
15    const [password, setPassword] = useState("");
16    const [name, setName] = useState("");
17    const [error, setError] = useState("");
18
19    const submitForm = () => {
20      if (email === "" || password === "" || name === "") {
21        setError("Fields are required");
22        return;
23      }
24      props.register({ email, password });
25    };
26
27    if (props.redirectTo) {
28      return <Redirect to={props.redirectTo} />;
29    }
30
31    return (
32      <form>
33        <Typography variant="h5" style={{ marginBottom: 8 }}>
34          Create an account
35        </Typography>
36        <TextField
37          label="Name"
38          variant="outlined"
39          fullWidth
40          className="form-input"
41          value={name}
42          onChange={e => setName(e.target.value)}
43        />
44        <TextField
45          label="Email"
46          variant="outlined"
47          fullWidth
48          className="form-input"
49          value={email}
50          onChange={e => setEmail(e.target.value)}
51        />
52        <TextField
53          label="Password"
54          variant="outlined"
55          fullWidth
56          className="form-input"
57          type="password"
58          value={password}
59          onChange={e => setPassword(e.target.value)}
60        />
61        <Button
62          variant="contained"
63          color="primary"
64          fullWidth
65          className="form-input"
66          size="large"
67          onClick={submitForm}
68        >
69          Register
70        </Button>
71
72        {(props.error || error) && (
73          <Alert severity="error" onClick={() => setError(null)}>
74            {props.error || error}
75          </Alert>
76        )}
77      </form>
78    );
79  }
80);
jsx

Redux Middlewares

As in this earlier guide, we are going to have two middleware functions. The first will handle network requests, and the second will handle app-specific requirements—in our case, registration.

app.js

In the app.js file, we will catch the REGISTER action, dispatch the API_REQUEST action using the apiRequest() action creator, and pass the user data into it.

1import { apiRequest } from "../actions/api";
2import { REGISTER } from "../actions/register";
3
4const SERVER_URL = `https://61m46.sse.codesandbox.io`;
5
6export const appMiddleware = () => next => action => {
7  next(action);
8  switch (action.type) {
9    case REGISTER: {
10      next(
11        apiRequest({
12          url: `${SERVER_URL}/register`,
13          method: "POST",
14          data: action.payload
15        })
16      );
17      break;
18    }
19    default:
20      break;
21  }
22};
js

core.js

In the core.js file, we will make the network request to post the registered user data to the server. If the request is successful, we will dispatch the API_SUCCESS action with the response in the payload and also at the same time dispatch the REDIRECT action with the redirect link.

1import axios from "axios";
2import { API_REQUEST, apiError, apiSuccess } from "../actions/api";
3import { redirect } from "../actions/ui";
4
5export const apiMiddleware = ({ dispatch }) => next => action => {
6  next(action);
7
8  if (action.type === API_REQUEST) {
9    const { url, method, data } = action.meta;
10    axios({
11      method,
12      url,
13      data
14    })
15      .then(({ data }) => {
16        dispatch(apiSuccess({ response: data }));
17        dispatch(redirect("/home"));
18      })
19      .catch(error => {
20        console.log(error);
21        dispatch(apiError({ error: error.response.data }));
22      });
23  }
24};
js

Main App Component

In the App.js file, we will wrap the components with the <Provider /> so that the Redux store is available to all the components in the application. We will have three routes or pages: the index page, which will be displayed initially; the registration page, which will be displayed when the user clicks on the Register button, and the home page, where the user will be redirected after a successful registration.

App.js

1import React from "react";
2import { BrowserRouter as Router, Switch, Route } from "react-router-dom";
3import "./app.css";
4import { Provider } from "react-redux";
5import { applyMiddleware } from "redux";
6
7import { createStore } from "redux";
8
9import NavBar from "./components/Nav";
10import { Typography, Divider } from "@material-ui/core";
11
12import HomePage from "./pages/HomePage";
13import RegisterPage from "./pages/Register";
14
15import { appMiddleware } from "./middlewares/app";
16import { apiMiddleware } from "./middlewares/core";
17
18import reducer from "./reducer";
19
20const createStoreWithMiddleware = applyMiddleware(
21  appMiddleware,
22  apiMiddleware
23)(createStore);
24
25const store = createStoreWithMiddleware(reducer);
26
27const IndexPage = () => (
28  <>
29    <Typography variant="h3">Welcome to the App</Typography>
30    <Divider style={{ marginTop: 10, marginBottom: 10 }} />
31    <Typography variant="h6">Feel free to take a look around</Typography>
32  </>
33);
34
35export default function App() {
36  return (
37    <Provider store={store}>
38      <Router>
39        <NavBar />
40        <div className="container">
41          <Switch>
42            <Route path="/register">
43              <RegisterPage />
44            </Route>
45            <Route path="/home" render={HomePage} />
46            <Route path="/" render={IndexPage} />
47          </Switch>
48        </div>
49      </Router>
50    </Provider>
51  );
52}
jsx

Conclusion

Using middlewares to redirect to other routes or pages is a relatively new concept, but it's a very scalable solution for robust and complex applications. Middlewares can also be used for reporting bugs, crash reports, logging data, etc. You must understand how middlewares work to write maintainable code without a third-party library, hence keeping the source code minimal and lightweight.

Do let me know if you have any other solutions to redirect a user by contacting me at CodeAlphabet.

That's it from this guide. Hope you are having a good time coding like a beast.