Let's build an app
index.js
Contacts.js
api.js
styles.css
index.html
README.md
index.js
import React from 'react'
import ReactDOM from 'react-dom'
import { createRecord, deleteRecord, getRecords, patchRecord } from './api'
import { Contact, ContactForm, ContactList } from './Contacts'

class App extends React.Component {
  constructor() {
    super()
    this.state = {
      contacts: [],
      contactsError: null,
      contactForm: {
        name: '',
        email: '',
      },
    }
    this.addContact = this.addContact.bind(this)
    this.changeContactForm = this.changeContactForm.bind(this)
    this.refresh = this.refresh.bind(this)
  }

  componentDidMount() {
    this.refresh()
  }

  render() {
    let content
    if (this.state.contactsError) {
      content = (
        <p>
          {this.state.contactsError}
        </p>
      )
    }
    else {
      content = this.state.contacts.map((contact, i) =>
        React.createElement(Contact, { ...contact, key: i })
      )
    }
  
    return (
      <ContactList onClickRefresh={this.refresh}>
        {content}
        <ContactForm
          errors={this.state.contactFormErrors}
          value={this.state.contactForm}
          onChange={this.changeContactForm}
          onSubmit={this.addContact}
        />
      </ContactList>
    )
  }
  
  changeContactForm(value) {
    this.setState({
      contactForm: value,
      contactFormErrors:
        silenceRectifiedErrors(this.state.contactFormErrors, validateContact(value))
    })
  }
  
  addContact() {
    let errors = validateContact(this.state.contactForm)
    if (!errors) {
      this.setState({
        contacts: this.state.contacts.concat(this.state.contactForm),
        contactForm: {},
      })
    }
    else {
      this.setState({
        contactFormErrors: errors,
      })
    }
  }

  refresh() {
    getRecords().then(
      (response) => {
        this.setState({
          contacts: response.data,
          contactsError: null,
        })
      },
      (error) => {
        this.setState({
          contactsError: "Your contacts couldn't be loaded :-("
        })
      }
    ) 
  }
}

function validateContact(contact) {
  let errors = {}
  if (!contact.name) {
    errors.name = "You must enter a name."
  }
  if (!contact.email) {
    errors.email = "You must enter an email."
  }
  else if (!isEmailValid(contact.email)) {
    errors.email = "That doesn't look like a valid e-mail."
  }
  if (Object.keys(errors).length) {
    return errors
  }
}

function isEmailValid(value) {
  return value.indexOf('@') !== -1
}

function silenceRectifiedErrors(oldErrors, newErrors) {
  if (newErrors && oldErrors) {
    let errors = {}
    for (let key of Object.keys(newErrors)) {
      if (oldErrors[key]) {
        errors[key] = oldErrors[key]
      }
    }
    return Object.keys(errors).length ? errors : null
  }
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
)