Dealing with state

Follow me on Twitter, happy to take your suggestions on topics or improvements /Chris

State is how we can change the data inside of a component. In the former section we covered props, Props . Props are great but they lack the ability to be changed in the component they are added in. Let's look at the following example so you understand what I mean:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Element extends React.Component {
  static propTypes = {
    name: PropTypes.string
  }

  // THIS WON'T WORK
  changeName() {
    this.props.name = 'new name';
  }

  render() {
    return (
      <div>{this.props.name}</div>
      <button onClick={() => this.changeName()} ></button>
    )
  }
}

// usage

let person = { name: 'chris' }

<Element name={person.name} />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

In the above example we try to change the name property but React won't let us do that. Instead we need to rely on state to do so.

Creating the state

There are two ways we can create the state:

  • declare it in the constructor
  • create it as inline field in the class

The first way looks like this:

class Element extends React.Component {
  constructor() {
    this.state = {
      field : 'some value'
    }
  }
}
1
2
3
4
5
6
7

The second way looks like this:

class Element extends React.Component {
  state = {
    field : 'some value'
  }
}
1
2
3
4
5

Accessing the state

Accessing the state is as simple as calling this.state.property. If you want to be more elegant you can use destructuring like so:

  render() {
  // DESTRUCTURING
    const { name } = this.props;

    return (
      <div>{name}</div>
      <button onClick={() => this.changeName()} >Change name</button>
    )
  }
1
2
3
4
5
6
7
8
9

Changing the state

To change our state we need to call the setState() method and provide it the slice of change we want to change here:

this.setState({
  name: 'new name'
})
1
2
3

One important thing to know though. If the state object is way bigger than that, like so:

state = {
  name : '',
  products: []
}
1
2
3
4

Only the part you refer to in setState() will be affected.

Rewriting our example to use state

Let's rewrite the above to instead use state:

import React, { Component } from 'react';
import PropTypes from 'prop-types';

class Element extends React.Component {
  static propTypes = {
    name: PropTypes.string
  }

  constructor() {
    this.state = {
      name : this.props.name 
    }
  }

  changeName() {
    this.setState({
      name: 'new name'
    })
  }

  render() {
    return (
      <div>{this.props.name}</div>
      <button onClick={() => this.changeName()} >Change name</button>
    )
  }
}

// usage

let person = { name: 'chris' }

<Element name={person.name} />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

Above we are now using the state functionality so we can change the value. We are however creating a copy of the props value in the constructor :

constructor() {
  this.state = {
    name : this.props.name
  }
}
1
2
3
4
5

Listen to the change

Changing the the state with setState() doesn't happen there and then, it takes a little time. So doing something like this may lead to buggy code:

someMethod() {
  this.setState({
    name : 'some value'
  });

  if(this.state.name === 'some value') {
    // do something
  }
}
1
2
3
4
5
6
7
8
9

It's usually better to wait until you are in the render() method and then do your comparison, like so:

import React, { Component } from 'react';

class Element extends React.Component {

  constructor() {
    this.state = {
      show : false
    }
  }

  toggleShow() {
    this.setState({
      show: !this.state.show
    })
  }

  render() {
    return (
      <div>Element</div>

      // better to access the state here, when it has its new value we act accordingly
      { this.state.show &&
      <div>show this..</div>
      }
      <button onClick={() => this.toggleShow()} >Toggle</button>
    )
  }
}

// usage

<Element />
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

If you really want to know exactly when the change in the state happens, you can provide a callback to setState(), like so:

someMethod() {

  this.setState({
    name : 'some value'
  }, () => {
    // state changed here
    if (this.state.name === 'some value') {
      // do something
    }
  });
}
1
2
3
4
5
6
7
8
9
10
11