React components can have a private state

The following is also only applicable to class components. Did I mention that some people call presentational-only components dumb?

The state property is a special one in any React class component. React monitors every component state for changes. But for React to do so efficiently, we have to change the state field through another React API thing that we need to learn, this.setState:

// Example 13 -  the setState API
// https://jscomplete.com/repl?j=H1fek2KH-
class CounterButton extends React.Component {
  state = {
    clickCounter: 0,
    currentTimestamp: new Date(),
  };
  
  handleClick = () => {
    this.setState((prevState) => {
     return { clickCounter: prevState.clickCounter + 1 };
    });
  };
  
  componentDidMount() {
   setInterval(() => {
     this.setState({ currentTimestamp: new Date() })
    }, 1000);
  }
  
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>Click</button>
        <p>Clicked: {this.state.clickCounter}</p>
        <p>Time: {this.state.currentTimestamp.toLocaleString()}</p>
      </div>
    );
  }
}
// Use it
ReactDOM.render(<CounterButton />, mountNode);

This is the most important example to understand. It will basically complete your fundamental knowledge of the React way. After this example, there are a few other small things that you need to learn, but it’s mostly you and your JavaScript skills from that point.

Let’s walk through Example 13, starting with class fields. It has two of them. The special state field is initialized with an object that holds a clickCounter that starts with 0, and a currentTimestamp that starts with new Date().

The second class field is a handleClick function, which we passed to the onClick event for the button element inside the render method. The handleClick method modifies this component instance state using setState. Take notice of that.

The other place we’re modifying the state is inside an interval timer that we started inside the componentDidMount lifecycle method. It ticks every second and executes another call to this.setState.

In the render method, we used the two properties we have on the state with a normal read syntax. There is no special API for that.

Now, notice that we updated the state using two different ways:

  1. By passing a function that returned an object. We did that inside the handleClick function.
  2. By passing a regular object. We did that inside the interval callback.

Both ways are acceptable, but the first one is preferred when you read and write to the state at the same time (which we do). Inside the interval callback, we’re only writing to the state and not reading it. When in doubt, always use the first function-as-argument syntax. It’s safer with race conditions because setState should always be treated as an asynchronous method.

How do we update the state? We return an object with the new value of what we want to update. Notice how in both calls to setState, we’re only passing one property from the state field and not both. This is completely okay because setState actually merges what you pass it (the returned value of the function argument) with the existing state. So, not specifying a property while calling setState means that we wish to not change that property (but not delete it).

Leave a comment

Your email address will not be published. Required fields are marked *