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.
Let's begin with action creators and a reducer.
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};
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};
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});
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;
In the next section, we will start writing 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// ..
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("");
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};
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}
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);
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.
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};
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};
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.
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}
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.