18
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.
1 2 3 4 5 6 7
export const REDIRECT = "REDIRECT"; // action creators export const redirect = link => { console.log("=== REDIRECT ACTION DISPATCHED ==="); return { type: REDIRECT, payload: link }; };
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.
1 2 3 4 5 6 7 8 9
export const REGISTER = "REGISTER"; export const register = user => { console.log("=== REGISTER ACTION DISPATCHED ==="); return { type: REGISTER, payload: user }; };
All the actions related to network requests like API_REQUEST
, API_SUCCESS
, and API_ERROR
will be included in the api.js
file.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
// action types export const API_REQUEST = "API_REQUEST"; export const API_SUCCESS = "API_SUCCESS"; export const API_ERROR = "API_ERROR"; export const CANCEL_API_REQUEST = "CANCEL_API_REQUEST"; // action creators export const apiRequest = ({ url, method, data }) => { return { type: API_REQUEST, meta: { url, method, data } }; }; export const cancelApiRequest = () => { return { type: CANCEL_API_REQUEST }; }; export const apiSuccess = ({ response }) => ({ type: API_SUCCESS, payload: response }); export const apiError = ({ error }) => ({ type: API_ERROR, payload: error });
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.
1 2 3 4 5 6 7 8 9 10
const reducer = (state = {}, action) => { switch (action.type) { case REDIRECT: return { redirectTo: action.payload }; default: return state; } }; export 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 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
// .. <form> <Typography variant="h5" style={{ marginBottom: 8 }}> Create an account </Typography> <TextField label="Name" variant="outlined" fullWidth className="form-input" value={name} onChange={e => setName(e.target.value)} /> <TextField label="Email" variant="outlined" fullWidth className="form-input" value={email} onChange={e => setEmail(e.target.value)} /> <TextField label="Password" variant="outlined" fullWidth className="form-input" type="password" value={password} onChange={e => setPassword(e.target.value)} /> <Button variant="contained" color="primary" fullWidth className="form-input" size="large" onClick={submitForm} > Register </Button> {(props.error || error) && ( <Alert severity="error" onClick={() => setError(null)}> {props.error || error} </Alert> )} </form> // ..
We'll create the form as a functional component and manage the state using the useState
hook.
1 2 3 4
const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [name, setName] = useState(""); const [error, setError] = useState("");
On Submit button click, we will do basic validations and dispatch the REGISTER
action.
1 2 3 4 5 6 7
const submitForm = () => { if (email === "" || password === "" || name === "") { setError("Fields are required"); return; } props.register({ name, email, password }); };
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
.
1 2 3
if (props.redirectTo) { return <Redirect to={props.redirectTo} />; }
So finally, our registration page will be as follows :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
import React, { useState } from "react"; import { TextField, Typography, Button } from "@material-ui/core"; import { connect } from "react-redux"; import { register } from "../actions/register"; import MuiAlert from "@material-ui/lab/Alert"; import { Redirect } from "react-router"; function Alert(props) { return <MuiAlert elevation={6} variant="filled" {...props} />; } export default connect(({ redirectTo }) => ({ redirectTo }), { register })( props => { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [name, setName] = useState(""); const [error, setError] = useState(""); const submitForm = () => { if (email === "" || password === "" || name === "") { setError("Fields are required"); return; } props.register({ email, password }); }; if (props.redirectTo) { return <Redirect to={props.redirectTo} />; } return ( <form> <Typography variant="h5" style={{ marginBottom: 8 }}> Create an account </Typography> <TextField label="Name" variant="outlined" fullWidth className="form-input" value={name} onChange={e => setName(e.target.value)} /> <TextField label="Email" variant="outlined" fullWidth className="form-input" value={email} onChange={e => setEmail(e.target.value)} /> <TextField label="Password" variant="outlined" fullWidth className="form-input" type="password" value={password} onChange={e => setPassword(e.target.value)} /> <Button variant="contained" color="primary" fullWidth className="form-input" size="large" onClick={submitForm} > Register </Button> {(props.error || error) && ( <Alert severity="error" onClick={() => setError(null)}> {props.error || error} </Alert> )} </form> ); } );
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
import { apiRequest } from "../actions/api"; import { REGISTER } from "../actions/register"; const SERVER_URL = `https://61m46.sse.codesandbox.io`; export const appMiddleware = () => next => action => { next(action); switch (action.type) { case REGISTER: { next( apiRequest({ url: `${SERVER_URL}/register`, method: "POST", data: action.payload }) ); break; } default: break; } };
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
import axios from "axios"; import { API_REQUEST, apiError, apiSuccess } from "../actions/api"; import { redirect } from "../actions/ui"; export const apiMiddleware = ({ dispatch }) => next => action => { next(action); if (action.type === API_REQUEST) { const { url, method, data } = action.meta; axios({ method, url, data }) .then(({ data }) => { dispatch(apiSuccess({ response: data })); dispatch(redirect("/home")); }) .catch(error => { console.log(error); dispatch(apiError({ error: error.response.data })); }); } };
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
import React from "react"; import { BrowserRouter as Router, Switch, Route } from "react-router-dom"; import "./app.css"; import { Provider } from "react-redux"; import { applyMiddleware } from "redux"; import { createStore } from "redux"; import NavBar from "./components/Nav"; import { Typography, Divider } from "@material-ui/core"; import HomePage from "./pages/HomePage"; import RegisterPage from "./pages/Register"; import { appMiddleware } from "./middlewares/app"; import { apiMiddleware } from "./middlewares/core"; import reducer from "./reducer"; const createStoreWithMiddleware = applyMiddleware( appMiddleware, apiMiddleware )(createStore); const store = createStoreWithMiddleware(reducer); const IndexPage = () => ( <> <Typography variant="h3">Welcome to the App</Typography> <Divider style={{ marginTop: 10, marginBottom: 10 }} /> <Typography variant="h6">Feel free to take a look around</Typography> </> ); export default function App() { return ( <Provider store={store}> <Router> <NavBar /> <div className="container"> <Switch> <Route path="/register"> <RegisterPage /> </Route> <Route path="/home" render={HomePage} /> <Route path="/" render={IndexPage} /> </Switch> </div> </Router> </Provider> ); }
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.
18