Author avatar

Gaurav Singhal

How to Use React Bootstrap with Redux

Gaurav Singhal

  • Sep 15, 2020
  • 15 Min read
  • 9,110 Views
  • Sep 15, 2020
  • 15 Min read
  • 9,110 Views
Web Development
React

Introduction

Bootstrap is the most popular CSS framework, used by over a million websites on the internet. So it's imperative to know how we can integrate this excellent CSS framework with our favorite JavaScript front-end stack—React and Redux.

One way to add Bootstrap into your app is by using CDN links. But, fortunately, there's also already an npm library out there that solves our problem.

React-Bootstrap

React-Bootstrap is the most popular front-end framework. It’s rebuilt for React.js—that is, it completely replaces Bootstrap Javascript. React-Bootstrap is an npm package that completely re-implements Bootstrap components for React and has no dependency on JQuery. It uses the state as supposed to direct DOM manipulation, which makes this package more reliable in the React world.

Installing React-Bootstrap

To install react-bootstrap, run the following command in your terminal.

1npm install react-bootstrap bootstrap
console

Note: Please make sure you have node installed in your system before running the command.

Example

For this guide, we will use a <LoginForm /> component from the React Bootstrap documentation and validate the fields using Redux.

Success output

Error output

LoginForm Component

We will import the <Form /> and <Button /> components from react-bootstrap and use them in our <LoginForm /> component.

1import Form from "react-bootstrap/Form";
2import Button from "react-bootstrap/Button";
3
4const LoginForm = () => (
5  <Form>
6    <h2>Login</h2>
7    <hr />
8    <Form.Group controlId="formBasicEmail">
9      <Form.Label>Email address</Form.Label>
10      <Form.Control type="email" placeholder="Enter email" />
11      <Form.Text className="text-muted">
12        We'll never share your email with anyone else.
13      </Form.Text>
14    </Form.Group>
15
16    <Form.Group controlId="formBasicPassword">
17      <Form.Label>Password</Form.Label>
18      <Form.Control type="password" placeholder="Password" />
19    </Form.Group>
20    <Form.Group controlId="formBasicCheckbox">
21      <Form.Check type="checkbox" label="Check me out" />
22    </Form.Group>
23    <Button variant="primary" type="button">
24      Submit
25    </Button>
26  </Form>
27);
28
29export default LoginForm;
jsx

Building the Store

In the global store object, we will store values and errors of the form.

1const initialState = {
2  loginForm: {
3    values: {
4      email: "",
5      password: ""
6    },
7    errors: {
8      email: "",
9      password: ""
10    }
11  }
12};
13
14export default (state = initialState, action) => {
15  if (action.type === "FORM_SUBMIT") {
16    const { email, password } = action.payload;
17    const values = {
18      email,
19      password
20    };
21    const errors = {}; // validate fields
22    return {
23      loginForm: {
24        values,
25        errors
26      }
27    };
28  }
29  return state;
30};
js

To validate the fields, we will write a validateEmail() function to check whether the email entered by the user is valid or not and whether the password has at least 8 characters.

1function validateEmail(email) {
2  var re = /^(([^<>()\[\]\\.,;:\[email protected]"]+(\.[^<>()\[\]\\.,;:\[email protected]"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
3  return re.test(String(email).toLowerCase());
4}
5
6const setErrors = (email, password) => {
7  let errors = { email: "", password: "" };
8  if (!email && email.length === 0) {
9    errors.email = "Email is required";
10  } else if (!validateEmail(email)) {
11    errors.email = "Email is invalid";
12  }
13  if (!password && password.length === 0) {
14    errors.password = "Password is required";
15  } else if (password.length < 8) {
16    errors.password = "Password must have 8 characters";
17  }
18  return errors;
19};
20
21// ...
22
23export default (state = initialState, action) => {
24  if (action.type === "FORM_SUBMIT") {
25    const { email, password } = action.payload;
26    const values = {
27      email,
28      password
29    };
30    const errors = setErrors(email, password); // validate fields
31    return {
32      loginForm: {
33        values,
34        errors
35      }
36    };
37  }
38  return state;
39};
js

Wrapping Root Component with Provider

To make the store accessible in all the components, we need to wrap the root component with the <Provider /> component from the react-redux package.

Also, notice that it's essential to import bootstrap CSS in the root component so that the styles are applied to the component.

1import "bootstrap/dist/css/bootstrap.min.css";
2import Card from "react-bootstrap/Card";
3import { Provider } from "react-redux";
4import { createStore } from "redux";
5
6import reducer from "./reducer";
7
8import LoginForm from "./LoginForm";
9
10const store = createStore(reducer);
11
12function App() {
13  return (
14    <Provider store={store}>
15      <div className="App">
16        <Card body>
17          <LoginForm />
18        </Card>
19      </div>
20    </Provider>
21  );
22}
jsx

Connecting <LoginForm /> Component with Redux

First, we have to make this a controlled component—that is, the form data must be controlled by the component's internal state.

1import React, { useState } from "react";
2
3const LoginForm = props => {
4  const [email, setEmail] = useState("");
5  const [password, setPassword] = useState("");
6  return (
7    <Form>
8      <h2>Login</h2>
9      <hr />
10      <Form.Group controlId="formBasicEmail">
11        <Form.Label>Email address</Form.Label>
12        <Form.Control
13          type="email"
14          placeholder="Enter email"
15          onChange={e => setEmail(e.target.value)}
16        />
17        <Form.Control.Feedback type="invalid">
18          {props.loginForm.errors.email}
19        </Form.Control.Feedback>
20        <Form.Text className="text-muted">
21          We'll never share your email with anyone else.
22        </Form.Text>
23      </Form.Group>
24
25      <Form.Group controlId="formBasicPassword">
26        <Form.Label>Password</Form.Label>
27        <Form.Control
28          type="password"
29          placeholder="Password"
30          onChange={e => setPassword(e.target.value)}
31        />
32        <Form.Control.Feedback type="invalid">
33          {props.loginForm.errors.password}
34        </Form.Control.Feedback>
35      </Form.Group>
36      <Form.Group controlId="formBasicCheckbox">
37        <Form.Check type="checkbox" label="Check me out" />
38      </Form.Group>
39      <Button variant="primary" type="button">
40        Submit
41      </Button>
42    </Form>
43  );
44};
jsx

We have discussed React hooks in an earlier (guide)/guides/change-page-background-color-each-route. Hooks are modern APIs provided by Redux to manage state in a functional component.

Next, we will connect the form to Redux using the connect() method.

1// ...
2import { connect } from "react-redux";
3
4const LoginForm = props => {
5  const [email, setEmail] = useState("");
6  const [password, setPassword] = useState("");
7  return (
8    <Form>
9      <h2>Login</h2>
10      <hr />
11      <Form.Group controlId="formBasicEmail">
12        <Form.Label>Email address</Form.Label>
13        <Form.Control
14          type="email"
15          placeholder="Enter email"
16          isInvalid={props.loginForm.errors.email.length > 0}
17          isValid={
18            props.loginForm.values.email &&
19            props.loginForm.errors.email.length === 0
20          }
21          onChange={e => setEmail(e.target.value)}
22        />
23        <Form.Control.Feedback type="invalid">
24          {props.loginForm.errors.email}
25        </Form.Control.Feedback>
26        <Form.Text className="text-muted">
27          We'll never share your email with anyone else.
28        </Form.Text>
29      </Form.Group>
30
31      <Form.Group controlId="formBasicPassword">
32        <Form.Label>Password</Form.Label>
33        <Form.Control
34          type="password"
35          placeholder="Password"
36          isInvalid={props.loginForm.errors.password.length > 0}
37          isValid={
38            props.loginForm.values.password &&
39            props.loginForm.errors.password.length === 0
40          }
41          onChange={e => setPassword(e.target.value)}
42        />
43        <Form.Control.Feedback type="invalid">
44          {props.loginForm.errors.password}
45        </Form.Control.Feedback>
46      </Form.Group>
47      <Form.Group controlId="formBasicCheckbox">
48        <Form.Check type="checkbox" label="Check me out" />
49      </Form.Group>
50      <Button
51        variant="primary"
52        type="button"
53        onClick={() =>
54          props.dispatch({ type: "FORM_SUBMIT", payload: { email, password } })
55        }
56      >
57        Submit
58      </Button>
59    </Form>
60  );
61};
62
63const mapStateToProps = state => ({
64  loginForm: state.loginForm
65});
66
67export default connect(mapStateToProps)(LoginForm);
jsx

To display whether a field is valid or not, we have to pass a Boolean value to the isValid and isInvalid props of the <Form.Control /> component.To display the error message, we will use the <Form.Control.Feedback /> component with type prop as invalid.

1<Form.Control.Feedback type="invalid">
2  {props.loginForm.errors.email}
3</Form.Control.Feedback>
jsx

Complete Source Code

LoginForm.js

1import React, { useState } from "react";
2import Form from "react-bootstrap/Form";
3import Button from "react-bootstrap/Button";
4import { connect } from "react-redux";
5
6const LoginForm = props => {
7  const [email, setEmail] = useState("");
8  const [password, setPassword] = useState("");
9  return (
10    <Form>
11      <h2>Login</h2>
12      <hr />
13      <Form.Group controlId="formBasicEmail">
14        <Form.Label>Email address</Form.Label>
15        <Form.Control
16          type="email"
17          placeholder="Enter email"
18          isInvalid={props.loginForm.errors.email.length > 0}
19          isValid={
20            props.loginForm.values.email &&
21            props.loginForm.errors.email.length === 0
22          }
23          onChange={e => setEmail(e.target.value)}
24        />
25        <Form.Control.Feedback type="invalid">
26          {props.loginForm.errors.email}
27        </Form.Control.Feedback>
28        <Form.Text className="text-muted">
29          We'll never share your email with anyone else.
30        </Form.Text>
31      </Form.Group>
32
33      <Form.Group controlId="formBasicPassword">
34        <Form.Label>Password</Form.Label>
35        <Form.Control
36          type="password"
37          placeholder="Password"
38          isInvalid={props.loginForm.errors.password.length > 0}
39          isValid={
40            props.loginForm.values.password &&
41            props.loginForm.errors.password.length === 0
42          }
43          onChange={e => setPassword(e.target.value)}
44        />
45        <Form.Control.Feedback type="invalid">
46          {props.loginForm.errors.password}
47        </Form.Control.Feedback>
48      </Form.Group>
49      <Form.Group controlId="formBasicCheckbox">
50        <Form.Check type="checkbox" label="Check me out" />
51      </Form.Group>
52      <Button
53        variant="primary"
54        type="button"
55        onClick={() =>
56          props.dispatch({ type: "FORM_SUBMIT", payload: { email, password } })
57        }
58      >
59        Submit
60      </Button>
61    </Form>
62  );
63};
64
65const mapStateToProps = state => ({
66  loginForm: state.loginForm
67});
68
69export default connect(mapStateToProps)(LoginForm);
jsx

On form submission, we will dispatch an FORM_SUBMIT action with the form values in the payload.

reducer.js

1function validateEmail(email) {
2  var re = /^(([^<>()\[\]\\.,;:\[email protected]"]+(\.[^<>()\[\]\\.,;:\[email protected]"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
3  return re.test(String(email).toLowerCase());
4}
5
6const initialState = {
7  loginForm: {
8    values: {
9      email: "",
10      password: ""
11    },
12    errors: {
13      email: "",
14      password: ""
15    }
16  }
17};
18
19const setErrors = (email, password) => {
20  let errors = { email: "", password: "" };
21  if (!email && email.length === 0) {
22    errors.email = "Email is required";
23  } else if (!validateEmail(email)) {
24    errors.email = "Email is invalid";
25  }
26  if (!password && password.length === 0) {
27    errors.password = "Password is required";
28  } else if (password.length < 8) {
29    errors.password = "Password must have 8 characters";
30  }
31  return errors;
32};
33
34export default (state = initialState, action) => {
35  if (action.type === "FORM_SUBMIT") {
36    const { email, password } = action.payload;
37    const values = {
38      email,
39      password
40    };
41    const errors = setErrors(email, password);
42    return {
43      loginForm: {
44        values,
45        errors
46      }
47    };
48  }
49  return state;
50};
js

index.js

1import React from "react";
2import ReactDOM from "react-dom";
3import "bootstrap/dist/css/bootstrap.min.css";
4import Card from "react-bootstrap/Card";
5import { Provider } from "react-redux";
6import { createStore } from "redux";
7
8import reducer from "./reducer";
9
10import LoginForm from "./LoginForm";
11
12const store = createStore(reducer);
13
14function App() {
15  return (
16    <Provider store={store}>
17      <div className="App">
18        <Card body>
19          <LoginForm />
20        </Card>
21      </div>
22    </Provider>
23  );
24}
25
26const rootElement = document.getElementById("root");
27ReactDOM.render(<App />, rootElement);
jsx

Conclusion

React Bootstrap allows us to quickly set up our application with a decent design and focus more on the business logic of the app. It also can be used as a starting point for building applications with complex UI as it's very flexible and can be highly customized.

I hope you like this guide. If you have any queries regarding this topic, feel free to contact me at CodeAlphabet.

To learn more, check out React Bootstrap Forms.