{ State. }

Objectives

By the end of this chapter, you should be able to:

  • Describe how props and state are different
  • Initialize state in a React component
  • Describe what a pure function is and how it applies to setState
  • Pass state down from a parent component to a child component via props

State

Props are great when you want to pass data to a component that isn't changing. However, when building dynamic web applications, we often want to be able to change our data. In React, we store dynamic data as state in the app. You may very often hear that "state is the root of all evil" and that is true to an extent, but it is often necessary when building applications. The reason why people are wary of including too much state in your application is that it adds complexity to your application. The more state you have to manage, the more difficult it becomes to reason about your application.

Important State Concepts:

  1. State should be owned by only one component. This is a crucial concept to keep in mind when you're designing your application. This simple rules makes React components much easier to debug and understand. When there is a problem, you typically know exactly which component is having an issue.
  2. When state is changed inside of a component, the render method will eventually be invoked by React. One of the key words here is eventually. Changes to state do not always show up right away (in practice they happen very quickly). This concept will lead to some rules we'll discuss about how state can change.

Initializing state

Let's create some state!

import React, {Component} from 'react'
import {render} from 'react-dom'

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      name: "Tim"
    };
  }
  render(){
    return (
      <div>
        <h2>Our state is {this.state.name}</h2>
      </div>
    );
  }
}

render(<App/>, document.getElementById("root"));

In the above example, we now have a use for the constructor function. We are setting up a state object for our App component. The state object is simply a normal JavaScript object.

Our example above doesn't really illustrate what state can do. So far the component won't actually change. Let's add a call to setState:

import React, {Component} from 'react'
import {render} from 'react-dom'

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      name: "Tim"
    };

    var changeName = function() {
      this.setState({name: "Whiskey"});
    };

    setTimeout(changeName.bind(this), 5000);
  }
  render(){
    return (
      <div>
        <h2>Our state is {this.state.name}</h2>
      </div>
    );
  }
}

render(<App/>, document.getElementById("root"));

Now our example is dynamic! In our constructor, we are creating a function, changeName that will call setState which will change the name property of our state object to Whiskey. Just below that, we have a setTimeout that will invoke changeName for us in 5 seconds (5000ms). The call to setState will trigger another call to render which will return new JSX which has this.state.name equal to Whiskey.

Now that we have seen setState, we need to take a moment to back up and review some functional programming concepts.

Functional Programming: Pure Functions

A pure function is a foundational concept in functional programing. The idea is that a function when invoked should not have any side effects and it should not modify the parameters it was given. Let's look at an example:

function doubleValues(arr){
  for (var i = 0; i < arr.length; i++) {
    arr[i] = arr[i] * 2;
  }
}

var arr1 = [2,4,5];
doubleValues(arr1);
doubleValues(arr1);

The example above is an impure function. The input array is being modified which breaks one of our pure function rules. One rule of thumb when testing for a pure function is to ask yourself, can the function be run multiple times and will I still get the same result? In our example above doubleValues is invoked twice and the result changes each time because arr1 is changing.

Here is an example of a pure function:

function doubleValues(arr){
  var res = [];
  for (var i = 0; i < arr.length; i++) {
    res.push(arr[i] * 2);
  }
  return res;
}

var arr1 = [2,4,5];
var result = doubleValues(arr1);
result = doubleValues(arr1);

Now if you look at the code above, we will always get the same result given the same input, no matter how many times the function is run. Since our input is not modified and the function has no other side effects, this is a pure function.

Let's look at one more example:

var myObj = {id: 53, hobby: "sailing"};
function addName(name){
  myObj.name = name;
}
addName("Tim");

Again, this is an impure function because there are side effects. We are modifying some global state which is not a parameter to the function. Let's refactor the code to pass in myObj as a parameter and we'll make sure not to modify the input:

var myObj = {id: 53, hobby: "sailing"};
function addName(obj, name){
  return Object.assign({}, obj, {name: name});
}
addName(myObj, "Tim");

Now we have a pure function because our function does not have side effects and the inputs are not being modified.

If you are not familiar with Object.assign, the function merges objects from right to left. So we are merging all of the keys and values from {name: name} into obj and then merging that result into our empty object, {}. Note that obj is never modified during this process. To learn more about Object.assign, check out the MDN docs.

Pure Functions and setState

Now we'll explore applying the pure function concept to how we will use setState. Let's imagine we have the following component:

import React, {Component} from 'react'
import {render} from 'react-dom'

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      people: [
                {name: "Tim"},
                {name: "Janey"},
                {name: "Kira"},
                {name: "Angelina"}
              ]
    };

    var addName = function() {
      var newPeople = this.state.people.slice();
      newPeople.push({name: "Whiskey"});
      this.setState({people: newPeople});
    };

    setTimeout(addName.bind(this), 5000);
  }
  render(){
    var peeps = this.state.people.map(function(p) {
      return <p key={p.name}>{p.name}</p>;
    });
    return (
      <div>
        {peeps}
      </div>
    );
  }
}

render(<App/>, document.getElementById("root"));

In our above example, we are adding a new person, Whiskey, to our array of name objects. The important piece of code to look at is the addName function. It is a pure function because we are not modifying the this.state.people array. Instead, we are using the slice method to create a copy of the array, and then we are adding our new name to the copy of our data.

This leads us to a rule with React:

Functions that call setState should be pure functions.

In other words, we never want to modify the state object directly. You may be wondering at this point, why do we care that calls to setState are pure? We'll discuss that next.

setState Is Asynchronous

As we alluded to earlier setState does not change the state of our app immediately. In fact, setState could be called multiple times before the state is actually updated and the DOM is re-rendered.

Let's take a look at our example again. If you were to add console.log(this.state.people) right after the invocation of setState, you would not see Whiskey in the people array.

  constructor(props){
    super(props);
    this.state = {
      people: [
                {name: "Tim"},
                {name: "Janey"},
                {name: "Kira"},
                {name: "Angelina"}
              ]
    };

    var addName = function() {
      var newPeople = this.state.people.slice();
      newPeople.push({name: "Whiskey"});
      this.setState({people: newPeople});
      // Whiskey will NOT be part of this.state.people yet
      console.log(this.state.people);
    };

    setTimeout(addName.bind(this), 5000);
  }

If for some reason you need to check the state after it has been updated, you can actually provide another parameter to setState which is a callback function that will be invoked once the state has been updated. Our setState code would now look like this:

var addName = function() {
  var newPeople = this.state.people.slice();
  newPeople.push({name: "Whiskey"});
  this.setState({people: newPeople}, () => {
    // Whiskey will now be included in the array!
    console.log(this.state.people);
  });
};

So because setState is asynchronous, you can't be sure when a render will happen. In other words, if you modify state directly you may end up confusing react and causing weird bugs. As long as you make sure you do not modify the state (keep your functions pure), then you will not run into problems.

Passing An Updater Function to setState

setState can also take an updater function rather than a new object for the state update. Whatever is returned from the function will be the new state. This is often useful when you want to know the previous version of the state:

import React, {Component} from 'react';

export default class Counter extends Component {
  constructor(props) {
    super(props);
    this.state = {
      counter: 1
    };

    setInterval(() => {
      this.setState((prevState, props) => {
        // Updating based on the previous value
        return {counter: prevState.counter + 1};
      }, () => {
        // Printing the update to the console
        console.log(this.state.counter);
      });
    }, 500);
  }

  render() {
    return <div>Counter: {this.state.counter}</div>;
  }
}

Notice in this example that we have access to prevState and props inside of the updater function to setState. We have also added a callback function after the state updater parameter that will console log the new state after it has been changed.

Passing State Down To Child Components As Props

Recall the first important rule we discussed about state: a piece of state should only be owned by one component. But many times you will want to provide data that is in one component to a child component. To pass state down to a child component, we will give the data to the child as a prop. Here's an example:

import React, {Component} from 'react'
import {render} from 'react-dom'


class Person extends Component {
  render() {
    var style={color: "red"};
    return <p style={style}>{this.props.name}</p>;
  }
}

class App extends Component {
  constructor(props){
    super(props);
    this.state = {
      people: [
                {name: "Tim"},
                {name: "Janey"},
                {name: "Kira"},
                {name: "Angelina"}
              ]
    };

    var addName = function() {
      var newPeople = this.state.people.slice();
      newPeople.push({name: "Whiskey"});
      this.setState({people: newPeople});
    };

    setTimeout(addName.bind(this), 5000);
  }

  render(){
    var peeps = this.state.people.map(function(p) {
      return <Person key={p.name} name={p.name} />;
    });
    return (
      <div>
        {peeps}
      </div>
    );
  }
}

render(<App/>, document.getElementById("root"));

You can see this in CodePen here

As you can see in this example, the state is still owned by the App component, but the data in the state is being passed to the Person component via the Person component's prop, name.

Additional reading

At this point you may be thinking to yourself "Okay, so when should I use props and when should I use state?" Developing an intuition for when to use each is one of the most important parts of becoming a successful React developer. This article (and the references therein) is a good place to start digging deeper.

Exercises

Complete the state exercises.

When you're ready, move on to React Component Architecture

Continue