Controlled Component in React Forms
April 7, 2020
Form controls in React come in two types: Controlled Component and Uncontrolled Component. In this video, we will focus only on the Controlled Component.
In a Controlled Component, the “single source of truth” (data) is maintained in the React state
property. An input form element whose value is controlled by React in this way is called a “controlled component”.
In HTML, form elements such as <input>
, <textarea>
, and <select>
typically maintain their own state and update it based on user input. In React, mutable state is typically kept in the state
property of components, and only updated with setState()
.
In a Controlled Component, form data is handled by a React component using an event handler (e.g. handleChange
) for every state update. Since handleChange
runs on every keystroke to update the React state
, the displayed value will update as the user types. This makes it straightforward to modify or validate user input. The following example illustrates this explanation.
Source code for demo in the video
import React from 'react'; //Parent component - contains main data class App extends React.Component { constructor(props) { super(props); this.state = { data: [] }; this.addData = this.addData.bind(this); } addData(newData){ let data = this.state.data; data.push(newData); this.setState({ data }); } render() { let data = this.state.data; return ( <div className="col-lg-12 text-center"> <h1>React Forms - Controlled Component</h1> <AddForm addData={this.addData} data={this.state.data}></AddForm> <hr></hr> <ul> {data.map( (e,i)=>{ return ( <li key={i}> {e.user} | {e.game} | {e.points} </li> ) }) } </ul> </div> ); } } // Child component - the Form to add one record // Controlled Component - source of truth in React class AddForm extends React.Component{ constructor(props){ super(props); this.state = { data : {user:'',game:'Galaga',points:0}} this.handleChange = this.handleChange.bind(this); this.handleSubmit = this.handleSubmit.bind(this); } handleChange(event){ let {name, value} = event.target; let data = this.state.data; data[name] = value; this.setState({data}) } error=false; handleSubmit(event){ //validate event.preventDefault(); if(!this.error){ this.props.addData(this.state.data); this.setState({ data : {user:'',game:'Galaga',points:0}}); } } render(){ let data = this.state.data; return( <div className="col-md-8 ml-5"> <form> <div className="form-row"> <div className="col-md-4 mb-3"> <label>User:</label> <input className="form-control" name="user" value={data.user} onChange={this.handleChange} /> </div> <div className="col-md-4 mb-3"> <label>Game:</label> <select className="form-control" name="game" defaultValue={data.game} onChange={this.handleChange} > <option>Pac-Man</option> <option>Pong</option> <option>Galaga</option> <option>Tetris</option> </select> </div> <div className="col-md-4 mb-3"> <label>Points:</label> <input className="form-control" name="points" type="number" min='0' max='10000' value={data.points} onChange={this.handleChange} /> </div> <div className="col-md-4 mb-3"> <button onClick={this.handleSubmit} className="btn btn-primary">Submit</button> </div> </div> </form> </div> ) } } export default App;