React Part II

Let's take a moment to think what this component should do:

it should:

  1. display the name and email of the contact

  2. allow the user to press an "edit" button, which opens an edit form

  3. in edit mode, when the user click "cancel", the edit form should close, and the content of it should reset

  4. in edit mode, when the user click "ok", a request should be sent to update the user

  5. if the user presses "delete", a request should be sent to delete the user

We can define two types of things here:

  1. name, email are properties held in the parent component, and not decided by our Contact component. They will be passed as props

  2. updateContact, deleteContact are methods in the parent component, and will be passed as props also.

  3. updateContact and deleteContact both need to pass an id property, which should be passed as a prop too.

  4. editMode is an internal state of the component. The parent component isn't concerned if the Contact component is opened or closed.

We have state, which means, we are going to use a class component.

Create a new file, Contact.js

Here's what we can write, based on what we know:

// front/src/Contact.js
import React from 'react'

export default class Contact extends React.Component{
  state = {
    editMode:false
  }
  toggleEditMode = () => {
    const editMode = !this.state.editMode
    this.setState({editMode})
  }
  renderEditMode(){
    const { id, name, email, updateContact, deleteContact } = this.props
    return (<div> edit mode</div>)
  }
  renderViewMode(){
    const { id, name, email, updateContact, deleteContact } = this.props
    return (<div>view mode</div>)
  }
  render(){
    const { editMode } = this.state
    if(editMode){
      return this.renderEditMode()
    }
    else{
      return this.renderViewMode()
    }
  }
}

We already have renderViewMode. It's what we were using previously in the App loop.

Let's copy paste it out of there, and modify it slightly:

...
renderViewMode(){
  const { id, name, email, deleteContact } = this.props
  return (
    <div>
      <span>
        {id} - {name}
      </span>
      <button onClick={() => deleteContact(id)} className="warning">x</button>
    </div>
  )
}
...

We still have to handle renderEditMode. Wait! It's suspiciously close to the "create" form! Let's copy-paste it from there. We need to operate a few changes though:

  1. Remove references to state and setState, since in this context, the values don't come from the state anymore.

  2. Change value to defaultValue. We want to set the starting value, but we do not want to track further state changes

  3. Add a new onSubmit function that will use updateContact

  4. Add a name property to each input, so we can find them back

...
  renderEditMode(){
    const { name, email } = this.props
    return(
    <form className="third" onSubmit={this.onSubmit} onReset={this.toggleEditMode}>
      <input
        type="text"
        placeholder="name"
        name="contact_name_input"
        defaultValue={name}
      />
      <input
        type="text"
        placeholder="email"
        name="contact_email_input"
        defaultValue={email}
      />
      <div>
        <input type="submit" value="ok" />
        <input type="reset" value="cancel" className="button" />
      </div>
    </form>
    )
  }
  onSubmit = (evt) => {
    // stop the page from refreshing
    evt.preventDefault()
    // target the form
    const form = evt.target
    // extract the two inputs from the form
    const contact_name_input = form.contact_name_input 
    const contact_email_input = form.contact_email_input 
    // extract the values
    const name = contact_name_input.value
    const email = contact_email_input.value
    // get the id and the update function from the props
    const { id, updateContact } = this.props
    // run the update contact function
    updateContact(id,{ name, email })
    // toggle back view mode
    this.toggleEditMode()
  }
...

Notice how the form's onReset toggles the edit mode too, so when a user clicks "cancel", the form switches back the view mode.

We just need something to switch from edit mode to view mode. Add a new button:

<button className="success" onClick={this.toggleEditMode}>edit</button>

Stick this somewhere next to the delete button.

The whole component will look like

Almost done, now, to see it, go to App.js, and do a little

// front/src/App.js
import Contact from './Contact'

at the top. Then, use it in the loop:

Replace where

...
  {contacts_list.map(contact => (
    <div key={contact.id}>
      <span>
        {contact.id} - {contact.name}
      </span>
      <button onClick={() => this.deleteContact(contact.id)} className="warning">x</button>
    </div>
  ))}
...

by

//front/src/App.js
{contacts_list.map(contact => (
  <Contact
    key={contact.id}
    id={contact.id}
    name={contact.name}
    email={contact.email}
    updateContact={this.updateContact}
    deleteContact={this.deleteContact}
  />
))}

Now you can:

  1. insert a new contact in the database

  2. delete a contact

  3. update a contact

  4. list all contacts

Congrats! You have a fully functional CRUD. This is not bad at all, ...for a start

Last updated