React State - Handling user form input

Linked Projects:

Calreact

✨ 2 people thanked Hrishi Mittal for this tutorial.

👍 Send Thanks
 
Lesson links:

Lesson code - https://github.com/learnetto/calreact/tree/lesson-3.3

Now we’ve got our components all set, but  before we make the form work we need to think about some key React concepts.

 By allowing user to add appointments we are going to introduce change. And so our app needs to manage state. The user will enter some data in the form fields and does change form state. When the form is submitted, a new appointment will be created and added to the list of appointments does changing its state. So the AppointmentForm and the AppointmentsLists components both will depend  on the state changes. But which one should manage the state? Let’s see what React Team recommends.

They say: 

For each piece of state in your application:
  • Identify every component that renders something based on that state.
  • Find a common owner component (a single component above all components that need the state in the hierarchy).
Let’s see what that means for our app.

Our Form component and AppointmentsList component both need to be render based on state. And their common owner the Appointments component which they are nested inside. So that’s where we will manage the state.

For managing the state your  first con to define initial state of the component using the getInitialState method. This method just returns an object with the state data. So let’s write this out here: 

(in appointmets.jsx)
getInitialState: function() {
  return {
    appointments: this.props.data
}
},

And it returns an object with the state data so we need set appointments to this.props.data  and that will be used by the appointments list component. Then  in Appointment Form component we need to manage the state of the two input fields. So let's add some initial data for those. So let's go back to Appointments component and add in here.  Let's call it  input_title and set it to empty string. And let's also set an input_appt_time.

(in appointmets.jsx)
getInitialState: function() {
  return {
    appointments: this.props.appointments,
    input_title: '',
    input_appt_data: 'Tommorow at 9am',
}
},

Now we need to change to change this value ( <AppointmetsList appointments={this.props.appointments} /> ) from this.props.appointments to this.state.appontments so it does reflects the lates data when the state changes. When the component is initialized we set the value of appointments to this.props.appointments which we got from as a prop from, when we called react_component helper method. Now let's pass the form fields state data as props to the appointment form component

render: function() {
     return (
       <div>
         <AppointmentForm input_title={this.state.title} input_appt_time={this.state.appt_time}/ >
         <AppointmentsList appointments={this.state.appointments} />
        </div>

Than we can use it in the component (appointment_form.jsx). 

 render: function() {
      return (
        <div>
          <h2>Make a new appointment</h2>
          <form>
            <input name='title' placeholder='Appointment Title'
                   value={this.props.input_title} />
            <input name='appt_time' placeholder='Date and Time'
                   value={this.props.input_appt_time} /> 
      <input type='submit' value='Make Appointment' />
          </form>        
        </div>
      )
    }

Let's also set an initial title:

input_title: 'Team standup meeting',

Now we reload the page and you can see our initial values in the form.

So our initial state is correctly set up, but we can't edit these  values. That's because React is setting them to the state, which we are not updating yet. So the form is stucked with initial values. Remember the only way we change the state in React is by using this.state. Of course we want to use it to be able to change the state - type in our form and make and appointments. Let's see how we gonna do that. 

If you are looking in developer console React is telling what we need to do. The first warning here says:
Failed form propType: you provided a 'value' prop to a form field without onChange handler. This will only render a read-only field. If the field should be mutable use 'defaultValue'. Otherwise, set either 'onChange' or 'readOnly'. Check the render method of "AppointmentForm'
This is one of the great things about React. It always gives you helpful warnings and error messages. It always nudges you into right direction, telling you which file or component you need to change to fix the problem. 

So let' s ahead and add the onChange attribute. Let's do the title first. We need to add  onChange here and set it to this.handleChange, where handleChange is the method that we will define here. It is going to pass the name and value of this input field to the appointments component through the callback method.  Where the name equals e.target.name. E is an event which is user typing something in and target is title input field and the name is the title. Then we will make an empty object and set its name key to the value of our input field - that's e.target.value. Then we call this.props.onUserInput with that object. onUserInput is an callback function which we will define in a moment. It will be passed to this component from the Appontments pomponent as a prop. 

var AppointmentForm = React.createClass({
   handleChange: function(e) {
   var name = e.target.name;
      obj = {};
      obj[name] = e.target.value;
   console.log(obj);
   console.log(this.props.onUserInput);
      this.props.onUserInput(obj);
    },
  
   render: function() {
      return (
        <div>
          <h2>Make a new appointment</h2>
          <form>
            <input name='title' placeholder='Appointment Title'
   value={this.props.input_title}
   onChange={this.handleChange} />
            <input name='appt_time' placeholder='Date and Time'
   value={this.props.input_appt_time}
    />
            <input type='submit' value='Make Appointment' />
          </form>        
        </div>
      )
    }
  });

So let's define it now. onUserInput equals this.handleUserInput, which is a callback function we are gonna now define. It will take the object containing the name and the value of the input field and change the state based on that by calling this.setState and passing the object.

var Appointments = React.createClass({
   getInitialState: function() {
   return {
   appointments: this.props.appointments,
   title: 'Team standup meeting',
   appt_time: '25 January 2016 9am'
      }
    },
  
   handleUserInput: function(obj) {
      this.setState(obj);
    },
  
   render: function() {
      return (
        <div>
          <AppointmentForm input_title={this.state.title}
   input_appt_time={this.state.appt_time}
   onUserInput={this.handleUserInput} />
          <AppointmentsList appointments={this.state.appointments} />
        </div>
      )
    }
  });

We still getting this warning, beacuse we haven't set onChange of the appointment time field yet. But we can edit the state this title field now. So let's now add onChange to the appointment time field as well.

 <input name='appt_time' placeholder='Date and Time'
   value={this.props.input_appt_time}
   onChange={this.handleChange}  />
Ok now the warning diapered and we can edit both the title field and the appointment time field. 

One thing I want to show you is the React Developer Tools browser extention in action. I've told you about it in the installation lesson. If you haven't got it yet, I highly recommend that you will install it. Now let's click on this React tab here. Now as I type you can see the live state changes here on the bottom right.  Every letter I type is changing the state and it's showing instantly in here. 

The other thing you can do is tick this box, which is says 'Trace React Updates' and then when you type you can also see this borders around the components which will be rendered based on the state change.

In the next lesson we will handle the form submition.

Liked this tutorial? Get more like this in your inbox