State

In React, state refers to mutable data that often changes as a result of user interaction. When it changes, the UI must be updated.

Originally, React class components were meant to be state-full components. Therefore, we currently work with state inside class components. However, this limitation was uplifted by the introduction of React Hooks (since React 16.8). Since then, function components can hold state too. Later, we will explore hooks and stateful function components.

import React, { Component } from 'react';

class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    }
  }

  incrementCount = () => {
    this.setState({
      count: this.state.count + 1
    });
  };

  render() {
    return (
      <>
        <div>Count is {this.state.count}</div>
        <button onClick={this.incrementCount}>+</button>
      </>
    );
  }
}

export default App;

Notice the incrementCount method uses this.setState to change the state. It is tempting to directly change the state as this.state.count += 1;. However, if you modify the state directly, React will not know about it! So instead, when you update the state, you must use the function setState (inherited from the React.Component class). When the setState is called, React knows the state is updated and will re-render the UI.

The setState function is asynchronous.

Due to its asynchronous nature, the value of this.state might not always hold the most up to date value. The setState provides an overloaded version that recieves a function agument as shown in the example below:

  incrementCount = () => {
    this.setState((state) => ({
      count: state.count + 1
    }));
  };

It is generally recommended to use this pattern if the new value of the state depends on its previous value.

Suppose you want to print out the value of count. Unfortunately, the following may not work correctly due to the asynchronous nature of the setState function.

  incrementCount = () => {
    this.setState((state) => ({
      count: state.count + 1
    }));
    console.log(this.state.count);
  };

Instead, you should use a callback pattern as follows:

  incrementCount = () => {
    this.setState((state) => ({
      count: state.count + 1
    })), () => console.log(this.state.count)
    );
  };