This post is part of the series 30 Days of React.

In this series, we're starting from the very basics and walk through everything you need to know to get started with React. If you've ever wanted to learn React, this is the place to start!

State

Edit this page on Github

Today we're getting started on how stateful components work in React and look at when and why we'll use state.

We've almost made it through the first week of getting up and running on React. We have worked through JSX, building our first components, setting up parent-child relationships, and driving our component properties with React. We have one more major idea we have yet to discuss about React, the idea of state.

The state of things

React does not allow us to modify this.props on our components for good reason. Imagine if we passed in the title prop to the Header component and the Header component was able to modify it. How do we know what the title is of the Header component? We set ourselves up for race-conditions, confusing data state, and it would be an all-around bad idea to modify a variable passed to a child component by a parent component.

However, sometimes a component needs to be able to update its own state. For example, setting an active flag or updating a timer on a stopwatch, for instance.

While it's preferable to use props as much as we can, sometimes we need to hold on to the state of a component. To handle this, React gives us the ability to hold state in our components.

state in a component is intended to be completely internal to the Component and its children (i.e. accessed by the component and any children it used). Similar to how we access props in a component, the state can be accessed via this.state in a component. Whenever the state changes (via the this.setState() function), the component will rerender.

For instance, let's say we have a simple clock component that shows the current time:

Even though this is a simple clock component, it does retain state in that it needs to know what the current time is to display. Without using state, we could set a timer and rerender the entire React component, but other components on the page may not need rerendering... this would become a headache and slow when we integrate it into a more complex application.

Instead, we can set a timer to call rerender inside the component and change just the internal state of this component.

Let's take a stab at building this component. First, we'll create the component we'll call Clock.

Before we get into the state, let's build the component and create the render() function. We'll need to take into account the number and prepend a zero (0) to the number if the numbers are smaller than 10 and set the am/pm appropriately. The end result of the render() function might look something like this:

class Clock extends React.Component {
  render() {
    const currentTime = new Date(),
          hours = currentTime.getHours(),
          minutes = currentTime.getMinutes(),
          seconds = currentTime.getSeconds(),
          ampm = hours >= 12 ? 'pm' : 'am';

    return (
      <div className="clock">
        {
          hours == 0 ? 12 :
            (hours > 12) ?
              hours - 12 : hours
        }:{
          minutes > 9 ? minutes : `0${minutes}`
        }:{
          seconds > 9 ? seconds : `0${seconds}`
        } {ampm}
      </div>
    )
  }
}

Alternative padding technique

Alternatively, we could use the short snippet to handle padding the clock time:

("00" + minutes).slice(-2)

But we've opted to be more clear with the previous code.

If we render our new Clock component, we will only get a time rendered everytime the component itself rerenders. It's not a very useful clock (yet). In order to convert our static time display Clock component into a clock that displays the time, we'll need to update the time every second.

In order to do that, we'll need to track the current time in the state of the component. To do this, we'll need to set an initial state value.

To do so, we'll first create a getTime() function that returns a javascript object containing hours, minutes, seconds and ampm values. We will call this function to set our state.

class Clock extends React.Component {
  //...
  getTime() {
    const currentTime = new Date();
    return {
      hours: currentTime.getHours(),
      minutes: currentTime.getMinutes(),
      seconds: currentTime.getSeconds(),
      ampm: currentTime.getHours() >= 12 ? 'pm' : 'am'
    }
  }
  // ...
}

In the ES6 class style, we can set the initial state of the component in the constructor() by setting this.state to a value (the return value of our getTime() function).

  constructor(props) {
    super(props);
    this.state = this.getTime();
  }

this.state will now look like the following object

{
  hours: 11,
  minutes: 8,
  seconds: 11,
  ampm: "am"
}

The first line of the constructor should always call super(props). If you forget this, the component won't like you very much (i.e. there will be errors).

Now that we have a this.state defined in our Clock component, we can reference it in the render() function using the this.state. Let's update our render() function to grab the values from this.state:

class Clock extends React.Component {
  // ...
  render() {
    const {hours, minutes, seconds, ampm} = this.state;
    return (
      <div className="clock">
        {
          hours === 0 ? 12 :
            (hours > 12) ?
              hours - 12 : hours
        }:{
          minutes > 9 ? minutes : `0${minutes}`
        }:{
          seconds > 9 ? seconds : `0${seconds}`
        } {ampm}
      </div>
    )
  }
}

Instead of working directly with data values, we can now update the state of the component and separate the render() function from the data management.

In order to update the state, we'll use a special function called: setState(), which will trigger the component to rerender.

We need to call setState() on the this value of the component as it's a part of the React.Component class we are subclassing.

In our Clock component, let's use the native setTimeout() JavaScript function to create a timer to update the this.state object in 1000 milliseconds. We'll place this functionality in a function as we'll want to call this again.

class Clock extends React.Component {
  // ...
  constructor(props) {
    super(props);
    this.state = this.getTime();
  }
  // ...
  componentDidMount() {
    this.setTimer();
  }
  // ...
  setTimer() {
    clearTimeout(this.timeout);
    this.timeout = setTimeout(this.updateClock.bind(this), 1000);
  }
  // ...
  updateClock() {
    this.setState(this.getTime, this.setTimer);
  }
  // ...
}

To start updating the timer immediately after the our component has been rendered, we call this.setTimer() in a React component lifecycle method called componentDidMount.We will get into the lifecycle hooks in the next section.

In the updateClock() function we'll want to update the state with the new time. We can now update the state in the updateClock() function:

class Clock extends React.Component {
  // ...
  updateClock() {
    this.setState(this.getTime, this.setTimer);
  }
  // ...
}

The component will be mounted on the page and will update the time every second (approximately every 1000 milliseconds)

Now the component itself might rerender slower than the timeout function gets called again, which would cause a rerendering bottleneck and needlessly using up precious battery on mobile devices. Instead of calling the setTimer() function after we call this.setState(), we can pass a second argument to the this.setState() function which will be guaranteed to be called after the state has been updated.

class Clock extends React.Component {
  // ...
  updateClock() {
    const currentTime = new Date();
    this.setState({
      currentTime: currentTime
    }, this.setTimer);
  }
  // ...
}

Here is our full Clock component code.

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = this.getTime();
  }
  
  componentDidMount() {
    this.setTimer();
  }

  setTimer() {
    clearTimeout(this.timeout);
    this.timeout = setTimeout(this.updateClock.bind(this), 1000);
  }

  updateClock() {
    this.setState(this.getTime, this.setTimer);
  }

  getTime() {
    const currentTime = new Date();
    return {
      hours: currentTime.getHours(),
      minutes: currentTime.getMinutes(),
      seconds: currentTime.getSeconds(),
      ampm: currentTime.getHours() >= 12 ? 'pm' : 'am'
    }
  }

  render() {
    const {hours, minutes, seconds, ampm} = this.state;
    return (
      <div className="clock">
        {hours == 0 ? 12 : hours > 12 ? hours - 12 : hours}:
        {minutes > 9 ? minutes : `0${minutes}`}:
        {seconds > 9 ? seconds : `0${seconds}`} {ampm}
      </div>
    );
  }
}

Styles

As we're not focusing on CSS in this course, we're not covering the CSS specific to build the clock as you see it on the screen.

However, we want to make sure the clock you build looks similar to ours. If you include the following CSS as a <link /> tag in your code, your clock will look similar and will be using the same styling ours is using:

<link href="https://cdn.jsdelivr.net/gh/fullstackreact/30-days-of-react@master/day-06/public/Clock.css" rel="stylesheet" type="text/css" />

Some things to keep in mind

  • When we call this.setState() with an object argument, it will perform a shallow merge of the data into the object available via this.state and then will rerender the component.

  • We generally only want to keep values in our state that we'll use in the render() function. From the example above with our clock, notice that we stored the hours, minutes, and seconds in our state. It's usually a bad idea to store objects or calculations in the state that we don't plan on using in the render function as it can cause unnecessary rendering and wasteful CPU cycles.

As we noted at the top of this section, it's preferred to use props when available not only for performance reasons, but because stateful components are more difficult to test.

Today, we've updated our components to be stateful and now have a handle on how to make a component stateful when necessary. Tomorrow we'll dive into the lifecycle of a component and when/how to interact with the page.

Learn React the right way

The up-to-date, in-depth, complete guide to React and friends.

Download the first chapter

Ari Lerner

Hi, I'm Ari. I'm an author of Fullstack React and ng-book and I've been teaching Web Development for a long time. I like to speak at conferences and eat spicy food. I technically got paid while I traveled the country as a professional comedian, but have come to terms with the fact that I am not funny.

Connect with Ari on Twitter at @auser.