In this guide, we will learn how to trigger a form submission by dispatching an action from a component outside the form. There are often situations where different child forms are related to each other but fall into different components. In this guide, we will discuss how to consolidate data from different child components and submit them.
two fields: email
and password
. On input change of these fields, we will dispatch an action to update the form values in the global store object.
Let's first get the primary form component right.
1class SignInForm extends Component {
2 constructor(props) {
3 super(props);
4 this.state = {
5 values: {
6 email: "",
7 password: ""
8 }
9 };
10 }
11
12 submitForm = e => {
13 e.preventDefault();
14 // dispatch FORM_SUBMIT action
15 };
16
17 handleInputChange = e =>
18 this.setState(
19 {
20 values: { ...this.state.values, [e.target.name]: e.target.value }
21 },
22 () => {
23 // dispatch SET_FORM_VALUES action
24 }
25 );
26
27 render() {
28 return (
29 <div>
30 <form onSubmit={this.submitForm}>
31 <div className="input-group">
32 <label htmlFor="email">E-mail Address</label>
33 <input
34 type="email"
35 name="email"
36 id="email"
37 value={this.state.values.email}
38 onChange={this.handleInputChange}
39 title="Email"
40 required
41 />
42 </div>
43 <div className="input-group">
44 <label htmlFor="password">Password</label>
45 <input
46 type="password"
47 name="password"
48 id="password"
49 value={this.state.values.password}
50 onChange={this.handleInputChange}
51 title="password"
52 required
53 />
54 </div>
55 <button type="submit">Sign In</button>
56 </form>
57 </div>
58 );
59 }
60}
61
62export default SignInForm;
As you can see in the above code, we are keeping the form values in the component state. However, for the form values to be accessible outside the form component, the values should be in the global store. Therefore, in the handleInputChange()
handler, we are going to dispatch an action with the form values in the payload.
Now, let's connect the <SignInForm />
component with Redux.
1import { connect } from "react-redux";
2
3class SignInForm extends Component {
4 submitForm = e => {
5 e.preventDefault();
6 this.props.dispatch({
7 type: "SUBMIT_FORM"
8 });
9 };
10
11 handleInputChange = e =>
12 this.setState(
13 {
14 values: { ...this.state.values, [e.target.name]: e.target.value }
15 },
16 () =>
17 this.props.dispatch({
18 type: "SET_FORMVALUES",
19 payload: this.state.values
20 })
21 );
22
23 render() {
24 return (
25 //
26 );
27 }
28}
29
30export default connect(null)(SignInForm);
Notice above in the handleInputChange()
method that we are dispatching the action with the form values in the payload
attribute.
Our reducer will be as follows (it's just a simple function that will return the state based on the action
type):
1const initialState = {
2 formValues: {},
3 message: ""
4};
5
6const reducer = (state = initialState, action) => {
7 switch (action.type) {
8 case "SET_FORMVALUES":
9 return {
10 ...state,
11 formValues: action.payload
12 };
13 case "SUBMIT_FORM":
14 console.log("--- Triggered Form submission ---");
15 console.log("Form Data - ", state.formValues);
16 return {
17 ...state,
18 message: "Form submitted!!"
19 };
20 default:
21 return state;
22 }
23};
24
25export default reducer;
For SUBMIT_FORM
action, we will display the values in the console. In a real application, though, you'd probably make an HTTP request to an external server to post the data or authenticate the user.
1import { Provider } from "react-redux";
2import { createStore } from "redux";
3
4import SignInForm from "./SignInForm";
5
6import reducer from "./reducer";
7
8const store = createStore(reducer);
9
10function App() {
11 return (
12 <Provider store={store}>
13 <div className="App">
14 <h1>Sign In To Your Account</h1>
15 <SignInForm />
16 </div>
17 </Provider>
18 );
19}
At this point, if you run this example and submit the form, you should be able to see the form values in the console .
Now that the form submission has been successfully triggered by the <SignInForm />
component, let's try and trigger the form submission from a separate component.
For that, let's create a simple <Button />
component and dispatch the SUBMIT_FORM
action in the button click handler.
1import { connect } from "react-redux";
2
3const Button = props => (
4 <div>
5 Click this to
6 <button onClick={e => props.dispatch({ type: "SUBMIT_FORM" })}>
7 Trigger Form submit
8 </button>
9 from outside the form
10 </div>
11);
12
13export default connect(null)(Button);
In the index.js
file, we will import this component and place it just after the <SignInForm />
component.
1// ...
2import Button from "./Button";
3
4function App() {
5 return (
6 <Provider store={store}>
7 <div className="App">
8 <h1>Sign In To Your Account</h1>
9 <SignInForm />
10 <Button />
11 </div>
12 </Provider>
13 );
14}
Now run the application again, but this time click on the second button. You should see the Triggered Form Submission
message in the console, same as before.
1import React from "react";
2import ReactDOM from "react-dom";
3import { Provider } from "react-redux";
4import { createStore } from "redux";
5
6import "./styles.css";
7
8import SignInForm from "./SignInForm";
9import Button from "./Button";
10
11import reducer from "./reducer";
12
13const store = createStore(reducer);
14
15function App() {
16 return (
17 <Provider store={store}>
18 <div className="App">
19 <h1>Sign In To Your Account</h1>
20 <SignInForm />
21 <Button />
22 </div>
23 </Provider>
24 );
25}
26
27const rootElement = document.getElementById("root");
28ReactDOM.render(<App />, rootElement);
1const initialState = {
2 formValues: {},
3 message: ""
4};
5
6const reducer = (state = initialState, action) => {
7 switch (action.type) {
8 case "SET_FORMVALUES":
9 return {
10 ...state,
11 formValues: action.payload
12 };
13 case "SUBMIT_FORM":
14 console.log("--- Triggered Form submission ---");
15 console.log("Form Data - ", state.formValues);
16 return {
17 ...state,
18 message: "Form submitted!!"
19 };
20 default:
21 return state;
22 }
23};
24
25export default reducer;
1import React, { Component } from "react";
2import { connect } from "react-redux";
3
4class SignInForm extends Component {
5 constructor(props) {
6 super(props);
7 this.state = {
8 values: {
9 email: "",
10 password: ""
11 }
12 };
13 }
14
15 submitForm = e => {
16 e.preventDefault();
17 this.props.dispatch({
18 type: "SUBMIT_FORM"
19 });
20 };
21
22 handleInputChange = e =>
23 this.setState(
24 {
25 values: { ...this.state.values, [e.target.name]: e.target.value }
26 },
27 () =>
28 this.props.dispatch({
29 type: "SET_FORMVALUES",
30 payload: this.state.values
31 })
32 );
33
34 render() {
35 return (
36 <div>
37 <form onSubmit={this.submitForm}>
38 <div className="input-group">
39 <label htmlFor="email">E-mail Address</label>
40 <input
41 type="email"
42 name="email"
43 id="email"
44 value={this.state.values.email}
45 onChange={this.handleInputChange}
46 title="Email"
47 required
48 />
49 </div>
50 <div className="input-group">
51 <label htmlFor="password">Password</label>
52 <input
53 type="password"
54 name="password"
55 id="password"
56 value={this.state.values.password}
57 onChange={this.handleInputChange}
58 title="password"
59 required
60 />
61 </div>
62 <button type="submit">Sign In</button>
63 </form>
64 <div className="message">
65 {this.props.message.length > 0 && this.props.message}
66 </div>
67 </div>
68 );
69 }
70}
71
72const mapStateToProps = state => ({
73 message: state.message
74});
75
76export default connect(mapStateToProps)(SignInForm);
1import React from "react";
2import { connect } from "react-redux";
3
4const Button = props => (
5 <div>
6 Click this to
7 <button onClick={e => props.dispatch({ type: "SUBMIT_FORM" })}>
8 Trigger Form submit
9 </button>
10 from outside the form
11 </div>
12);
13
14export default connect(null)(Button);
1.App {
2 font-family: sans-serif;
3}
4
5.input-group {
6 margin-bottom: 10px;
7}
8
9.input-group label {
10 display: block;
11 margin-bottom: 5px;
12}
13
14button {
15 border: none;
16 padding: 8px 24px;
17}
18
19.message {
20 margin-top: 20px;
21 font-weight: 600;
22}
23
24.message.error {
25 color: red;
26}
In this guide, we learned how to dispatch actions from other components and submit form values stored in the global store. Many libraries, such as redux-form
, work based on similar principles. I hope you're getting better at the Redux game every day. Until next time, keep hustling.