React State - Handling form submission

Lesson links:

Lesson code -

In this lesson, we'll add the final piece to make this form work. When the user submits the form, we'll handle the form submission and add a new appointment record. We're going to do that by setting the onSubmit attribute on the form and passing it another callback method. 


Which we'll define up here as a method which takes from submission event, "e", first prevents the default submission, so that we can pass it the callback method that we'll get from the appointments component. 

handleSubmit: function(e) {

Now let's define that in appointments.jsx. Set the prop "onFormSubmit" to "handleFormSubmit" and let's define "handleFormSubmit" as a method which posts an AJAX request to the appointments controller. 


 The endpoint we want to hit is "appointments" and we have to pass it the new appointment data. So let's define that here as a variable then include that in the AJAX call. 

handleFormSubmit: function() {
  var appointment = {title: this.state.title, appt_time: this.state.appt_time}
    {appointment: appointment})

Notice that we're not dealing with the response of the AJAX call. Even if the new appointment gets added successfully, it's not going to appear on our list on the webpage yet. 

Now let's try this. Enter a title, date and time and submit the form. It got posted but the page didn't reload and the new record didn't appear. But if we reload the page, we can see it here. So now let's do the next part - handing the response from the controller. Remember when we made this form in the first lesson, we added the create.js.haml file to handle the response. We don't need this file anymore, so let's delete it. 

Now here, let's add:



This is to deal with the response data, which we'll set in the controller. 

Let's change this to:

@appointment =

and add:

  render json: @appointment
  render json: @appointment.errors, status: :unprocessable_entity

Finally, we don't need to send all the appointments again. We're just going to send the new appointment and use React to insert it in the correct place on the page. So let's remove:

@appointments = Appointment.order('appt_time ASC')

Now back in the appointments component, let's do:


where "addNewAppointment" is a method we're going to define here - which will take the new appointment data, add it to the list of appointments and sort it by appointment time so it's displayed in the right order. Remember - we're listing the appointments in chronological order, so we need to make sure it goes in the right place.

addNewAppointment: function(appointment) {


Before we can do this, we need to enable React Add-Ons. React Add-Ons are a collection of useful utility modules for building React apps. They include things like animations, transitions and immutable data updates. We can enable React Add-Ons in the application.rb config file. 

config.react.addons = true

Now let's use it. Make a variable "appointments" which will be the area for appointments. 

addNewAppointment: function(appointment) {
  var appointments = React.addons.update(this.state.appointments, { $push: [appointments]});

The update method here is a way to efficiently create an area for appointments using the "push" command. This is React's way of mutating a copy of the data without changing the original. Using immutable data can help speed up your app a lot. The syntax is a bit unusual and so I recommend reading the documentation to get used to it. 

Now let's update the state with:

this.setState({ appointments: appointments});

Now let's try it - add a title, a date and time and click "make appointment". And nothing happened. Let's check the console to see if we got any warnings. We're getting a couple of warnings. The first one says "Each child in an array or iterator should have a unique "key" prop. Check the render method of 'AppointmentsList'." 

In order to make changes to the DOM, React requires that when you have a list of components, each of them should have a unique key. Keys help React identify which items have changed, added or need to be removed. So let's fix that in the "AppointmentsList" component.  We'll add a key here and since appointment is a database record, we can simply use its ID as a key because it's unique to each record.

<Appointment appointment={appointment} key={} />

That should fix the first warning. Now let's look at the second one - "TypeError: this.addNewAppointment is not a function". I wonder why it's saying that. Let's have a look at our appointments component. The "this" here is bound to the wrong scope. We need to bind it to the outer scope to make sure we're defining "addNewAppointment" as a method on the appointments component. We can fix that by adding:


One other thing I want to change quickly is rename these props without the "input_" prefix. It doesn't really matter but we don't need them. So I'm going to remove that to make it look cleaner. 

Ok - let's reload and try again. Say "breakfast meeting with hiring manager", "Tomorrow at 8am", and now I'm click this and it should work. So not it's getting added to the list but it's getting added to the bottom of the list. Because we're simply pushing it to the array for appointments, we're not sorting it again. So let's fix that.

We'll add: 

appointments: appointments.sort(function(a,b){
  return new Date(a.appt_time) - new Date(b.appt_time);

and that should sort our array by appointment time. Let's try this again - add a title and a date time and that should appear between these 2 appointments. So let's click on "make appointment" and there we go! There's a new appointment in the right place. We could do another one where we add a really old appointment... Let's say "go to football match" and let's set the date to 2014 so that should appear at the top of the list. There we go. 

So there's a fully-functioning appointments app. Of course we can still make some improvements - like formatting the dates nicely, and adding an interactive datePicker. So let's do that next... 

Liked this tutorial? Get more like this in your inbox