Editing a record - Part 1

Linked Projects:

Lesson code - https://github.com/learnetto/calreact/tree/m7-react-router

Now we’ll add a way to edit an appointment.

Right now we can make a new appointment and view it, but we haven’t added a way to change it.

First, let’s add a new route for editing an appointment.

Let’s make a copy of the show route and add slash edit to the url and change the component to AppointmentForm:

<Route path="/appointments/:id/edit" component={AppointmentForm} />

Let’s import it at the top:

import AppointmentForm from './AppointmentForm';

Now let’s add a link to navigate to the edit route. 

Let’s add it in the Appointment component at the bottom:

<Link to={`/appointments/${this.state.appointment.id}/edit`} >

Ok, so the link shows up.

Now when I click on it, you can see the url changes, but nothing else has changed on the page.

We’re just seeing the appointment.

In the console, we’re getting a bunch of failed proptype warnings and an error, because we’re not passing any required props to AppointmentForm.

Remember, we specified all these proptypes which are required and expect certain types of values.

So we need to fix that.

The other issue is that the route is also matching the show route.

So let's add "exact" to the show route:

<Route exact path="/appointments/:id/edit" component={AppointmentForm} />

Now if I refresh and go to edit, we get a blank page with just a header.

One other thing I want to change while we are adding a new route is to put "AppHeader" in a route as well, so it will appear in all the routes:

<Route path="/" component={AppHeader} />

Now let's fix the AppointmentForm on the edit route.  

Remember, when we use the AppointmentForm in our Appointments component, we pass a number of props. But in our edit route, we're simply rendering the component without passing any props. 

Now we could pass props using a function in the render prop like we did previously. But we're not going to do that. We're going to instead change the way our AppointmentForm component works.

Currently, we're managing the state for the form in the Appointments component because in the home page, it's nested in that component. But now, when the user goes to the edit route, we won't have that state management available, because there is no Appointments component on that route.

So we need to move the state management for the form to the AppointmentForm component itself. This will make the component reusable across routes.

If we look in the Appointments component constructor, we can take all the form-specific state properties and move them to the form component. We only need the appointments property to remain to update the list of appointments.

Of course, we could have started off by putting the form state directly in the AppointmentForm component, but I chose to put all the state management in the parent Appointments component to keep things simple. 

That way we could make all the other components stateless functional components and we had less code to write, and the app's structure was simpler.

But now that we are adding more features, we need to change things.

So we need to move the initial state values to AppointmentForm, and also all the other user input handling and validation methods.

The only thing we'll keep in the Appointments component are the things which are relevant to the full list of appointments. For example, addNewAppointment, we'll keep this here, because it updates the value of appointments in the state, which then re-renders the list of appointments on the home page. And later on, we'll also add a method for deleting appointments here.

So let's start by copying the constructor from Appointments and paste it into AppointmentForm.

Then back in Appointments, let's remove the form-specific values we don't need anymore from the constructor, like title, appt_time, formErrors, and formValid.

We won't be passing the props in the render method, so let's remove them too.

Let's move these methods to the form component:


We need to pass addNewAppointment as a prop, so that it gets called once a new appointment is added.

<AppointmentForm handleNewAppointment={this.addNewAppointment} />

And let's change the addNewAppointment method to:

addNewAppointment = (appointment) => {...

In AppointmentForm, we don't need any of the propTypes we currently have, so delete them. The only one we need is a new one for handleNewAppointment, which needs to be a function, but we won't make it required:

handleNewAppointment: PropTypes.func

We don't need appointments in state, so remove it.

Now let's fix the handleFormSubmit method.





Then in handleChange,  handleSubmit, and setApptTime, we don't need props anymore, so remove them. For example, handleSubmit will now go from:




And in the render method, "props" needs to be changed to "state" because we're holding the input-free state in this component now.

Let's just go to the home page to see if that still works. We get a "Failed prop type" warning for formErrors. I think we should move formErrors into the AppointmentForm component,  because we'll need it for the edit page as well.

So let's just put it in AppointmentForm above the form. And we need to import formErrors, so let me move that from the Appointments component into AppointmentForm.

Now when we try to refresh, the home page looks fine. Now let's try to make an appointment. Add a title, choose a date...there seems to be an issue. We get an error:

this.onUserInput is not a function

We need to change both instances to:


And let's also change this.handleSubmit to this.handleFormSubmit.

handleSubmit is actually redundant now so we can remove it. 

Let's modify handleFormSubmit:

handleFormSubmit = (e) => {

The other thing we need to do is import the update method in AppointmentForm:

import update from 'immutability-helper'

And we don't need to import moment in Appointments, so let's remove that.

Now I think we can make an appointment. Choose a title, select a date, submit, and there we go. It works just fine.

Liked this tutorial? Get more like this in your inbox