Lifecycle methods

Lifecycle methods
WIP

When you define a class component, you must always define a render() method, as React calls it to find out what to render.

But render() isn’t the only method that components can define!

When React instantiates a class component, it checks to see which lifecycle methods the class defines. You’ve already seen one of these: the render() method. But the other lifecycle methods are optional; React only calls them if they’re defined. They include:

  • componentDidMount() — is called after the inital content has been rendered to the DOM.
  • componentDidUpdate(prevProps) — is called after subsequent updates are rendered to the DOM, and is passed the previous value of this.props.
  • componentWillUnmount() — is called before the component is removed from the DOM.

Lifecycle methods are useful for interacting with the “outside world” in response to changes in a React component. For example, you could define a componentDidUpdate() method that changes your page’s background to a random color each time the form updates — no matter where the update comes from!

index.js
styles.css
import React from 'react'
import ReactDOM from 'react-dom'
import './styles.css'

class ContactForm extends React.Component {
  constructor() {
    super()
    this.state = {
      name: "",
      nameError: null,

      email: "",
      emailError: null,
     }
    this.handleChangeName = this.handleChangeName.bind(this)
    this.handleChangeEmail = this.handleChangeEmail.bind(this)
    this.handleSubmit = this.handleSubmit.bind(this)
  }


  componentDidUpdate() {
    let r = Math.floor(Math.random()*255)
    let g = Math.floor(Math.random()*255)
    let b = Math.floor(Math.random()*255)
    document.body.style.backgroundColor = `rgb(${r}, ${g}, ${b})`
  }

  render() {
    return (
      <form
        className='ContactForm'
        onSubmit={this.handleSubmit}>
        <label>
          <span>Name</span>
          <input
            value={this.state.name}
            onChange={this.handleChangeName}
          />
          {this.state.nameError && (
            <div className='ContactForm-error'>
              {this.state.nameError}
            </div>
          )}
        </label>
        <label>
          <span>E-mail</span>
          <input
            value={this.state.email}
            onChange={this.handleChangeEmail}
          />
          {this.state.emailError && (
            <div className='ContactForm-error'>
              {this.state.emailError}
            </div>
          )}
        </label>
        <button type="submit">
          Add
        </button>
      </form>
    )
  }
  
  handleChangeName(event) {
    this.setState({
      name: event.target.value,
      nameError: null,
    })
  }
  
  handleChangeEmail(event) {
    let value = event.target.value
  
    this.setState({
      email: value,
      emailError: isEmailValid(value) ? null : this.state.emailError,
    })
  }
  
  handleSubmit(event) {
    event.preventDefault()
    
    let errors = {}
    if (!this.state.name) {
      errors.nameError = "You must enter a name."
    }
    if (!this.state.email) {
      errors.emailError = "You must enter an email."
    }
    else if (!isEmailValid(this.state.email)) {
      errors.emailError = "That doesn't look like a valid e-mail."
    }
   
    if (Object.keys(errors).length === 0) {
      this.props.onAddContact({
        name: this.state.name,
        email: this.state.email,
      })
    }
    else {
      this.setState(errors)
    }
  }
}

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

ReactDOM.render(
  <ContactForm onAddContact={() => {}} />,
  document.getElementById('root')
)
Build In Progress

There’s one thing to be careful about when using componentDidUpdate(). If you call this.setState() inside it, then you’ll cause another update, and thus an infinite loop! You can use this.setState(), but you should only do so inside an if statement that prevents it from being called recursively.

#Fetching data on load

In the real world, lifecycle methods are often used to call networking code as components are mounted, updated, and unmounted. For example, you could use a lifecycle method to automatically fetch data once your contact list app has loaded.

In fact, this sounds like a great exercise!

Your task is to fetch initial data using a lifecycle method.

index.js
Contacts.js
api.js
styles.css
index.html
import React from 'react'
import ReactDOM from 'react-dom'
import { getRecords } from './api'
import { Contact, ContactForm, ContactList } from './Contacts'

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

  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 onAddContact={(contact) => {
          this.setState({
            contacts: this.state.contacts.concat(contact)
          })
        }} />
      </ContactList>
    )
  }

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

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

Finishing this exercise off should only take a few lines of new code. If you get stuck, use a bit of trial and error before checking the solution — you should be able to make it work.

And once it does work, then you’re pretty much done. Like, actually done. Lifecycle methods are the final React feature we’ll cover in this course! But there are still some important loose ends to tie up, so click on through to the next lesson.

Progress to next section.