Component state

The wrong way

Let's first try to simply add a code by updating the array of codes, and then log the updated codes. We expect the new code to appear below the third code.

function CodeListEditor() {
  const codes = [{
    id: 'code_1',
    label: 'unhappy'
  }, {
    id: 'code_2',
    label: 'happy',
  }, {
    id: 'code_3',
    label: 'very happy'
  }]
  const handleClick = () => {
    codes.push({ id: 'code_4', label: 'very unhappy' })
    console.log('codes:', codes)
  }
  return (
    <div>
      <button onClick={handleClick} />
      ...
    </div>
  )
}

As you notice, it does not quite work: the codes seem to have been updated (there are now 4 codes in our array), but the UI stayed the same.

This is a key feature of React: the app will only be re-rendered when a setState method is called somewhere. To use the setState method, we will need to make some adjustments to our CodeListEditor.

The right way

To use the setState method aforementioned, we need to use a different syntax to describe the component:

import React from 'react'
class CodeListEditor extends React.Component {
  render() {
    const codes = ...
    return (
      <div>
        <button onClick={handleClick} />
        ...
      </div>
    )
  }
}

We've just copied the body from our component function to the body of another function: the render method of a ES6 class which extends React.Component.

For now, nothing has changed: the UI is still not updating when we try to add a code. But this syntax exposes some additional functionalities. Among them is the option to call the setState method to tell the component something has changed and it should be re-rendered.

import React from 'react'
class CodeListEditor extends React.Component {
  constructor() {
    super()
    //we initialize the state with the 3 codes
    this.state = {
      codes: [{
        id: 'code_1',
        label: 'unhappy'
      }, {
        id: 'code_2',
        label: 'happy',
      }, {
        id: 'code_3',
        label: 'very happy'
      }]
    }
    this.addCode = () => {
      const { codes } = this.state
      const newId = `code_${codes.length+1}`
      //Instead of updating the codes in place, with `codes.push`, we prefer to
      //make a copy of the initial codes and append the new code at the end.
      const newCodes = [...codes, { id: newId, label: 'very unhappy' }]
      //we can now use `setState` to make this new array of codes part of the
      //component state. The `setState` call will trigger a re-rendering of the
      //component.
      this.setState({
        codes: newCodes
      })
    }
  }
  render() {
    //codes should now be extracted from the component state
    const { codes } = this.state
    return (
      <div>
        {/* we use the `addCode` method defined in the constructor */}
        <button onClick={this.addCode}>Add a code</button>
        <div>
        {
          codes.map(({ id, label }) => <CodeEditor key={id} label={label} />)
        }
        </div>
      </div>
    )
  }
}

We intitialize the state of the CodeListEditor component with the 3 already created codes. We then define a addCode function (see arrow functions) which:

  • creates an id for the newly created code;
  • creates a copy of the initial codes with a new code at the end (see spread operator) ;
  • calls setState to update the state of the component; the setState call will trigger a re-rendering of the component.

Now, when we click on the "Add a code" button, the UI updates accordingly.

Play with this pen.

results matching ""

    No results matching ""