Repository navigation
Edit a Contact
Up until now, we have most of the CRUD functionalities going on: we can create new contacts, delete existing contacts and list them to the screen. Now we need only one thing: to be able to update contact data.
For that, we will cover mostly what's remaining in our guide: dynamic routes with react-router,
component lifecycle methods and more state manipulation with Redux.
In order to identify which record we're going to edit, we need a unique ID that sets the contact records apart, since none of the fields are truly unique.
For that, we can use the uuid library that can be installed via yarn.
yarn add uuid
nano src/actions/index.jsimport uuid from 'uuid';
...
export const contactAdd = contact => ({
type: 'CONTACT_ADD',
contact: {
id: uuid.v4(),
...contact
}
});
...To implement the new update functionality, we need to create a new action, CONTACT_EDIT
with the updated contact as argument and wire it up on contactsReducer. Here's how we're
going to make it.
nano src/actions/index.js...
export const contactEdit = contact => ({
type: 'CONTACT_EDIT',
contact
});nano src/reducers/contactsReducer.js...
case 'CONTACT_EDIT':
const index = state.contactList.findIndex(c => c.id === action.contact.id);
return {
...state,
contactList: [
...state.contactList.slice(0, index),
action.contact,
...state.contactList.slice(index + 1)
]
};
...In the example above, we're looking for the index of the object that matches the unique ID to the one that we provided through the action and sliding it into the new state.
Now let's come up with the ContactEdit stateful component. The route we will assign to ContactEdit
has a dynamic parameter that we can access in the component via this.props.match.params. In this example,
since we still don't have a backend server doing the database work for us, we will also include
the contact list in the state so we can perform a search, but it will be removed later on.
We will also use a component lifecycle method called componentWillMount that's automatically
called prior to the first time the component renders on the screen. Inside it, we will locate
the right contact based on the contactId passed via route (that we will create later) and
update the Redux state accordingly.
nano src/containers/ContactEdit.jsimport React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link, withRouter } from 'react-router-dom';
import { contactFormUpdate, contactEdit } from '../actions';
import ContactForm from './ContactForm';
class ContactEdit extends Component {
state = {
contact: {}
}
componentWillMount() {
const { contactId } = this.props.match.params;
const contact = this.props.contactList.find(c => c.id === contactId);
this.setState({ contact });
}
handleSubmit(values) {
const { id, name, phone, email } = values;
this.props.onSubmit({ id, name, phone, email });
this.props.push("/");
}
render() {
return (
<ContactForm onSubmit={this.handleSubmit.bind(this)} initialValues={this.state.contact} />
)
}
}
const mapStateToProps = state => ({
...state.contacts
});
const mapDispatchToProps = dispatch => ({
onChange: ({ prop, value }) => dispatch(contactFormUpdate({ prop, value })),
onSubmit: contact => dispatch(contactEdit(contact))
});
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(ContactEdit));nano src/containers/ContactForm.js...
<form ...>
<Field type="hidden" component="input" name="id" />
...The id field is needed to store the newly created ID that will be needed to edit a contact.
Now that the ContactEdit component is ready, we need to set up a route for it on App.
nano src/App.js...
import ContactEdit from './containers/ContactEdit';
...
<Route path="/edit/:contactId" component={ContactEdit} />
.../edit/:contactId means that anything can go after /edit/ and that value will be captured
by the component through this.props.params.contactId, which we did in the ContactEdit
component. So, everytime we need to edit a contact, we just need to pass the unique ID after
/edit/ and ContactEdit will know it.
Now, let's include an edit link to the ContactList component.
nano src/containers/ContactList.js...
<th colSpan={2}> </th>
</tr>
</thead>
...
{
this.props.contactList.map(({ id, name, phone, email }, index) => (
<tr key={index}>
...
<td>
<Link to={`/edit/${id}`} className="btn btn-primary">Edit</Link>
</td>
<td>
<Button
buttonType="btn-danger"
onClick={() => this.props.onClickDelete(index)}
>
Delete
</Button>
</td>
</tr>
))
}
...Save, run yarn start and test.
Now we have a complete CRUD... except we still don't have a database. Commit the changes and let's do the next step.
git add .
git commit -m "Add an edit form and new routes to the app"Next step: Create a back-end server and database