Component state

Creating stateless components is ideal for simplicity and testability (among other benefits), but of course, it isn't always possible. React allows components to maintain their own internal state as an object with arbitrary keys.

Whenever the state or props of a component change, a rendering cycle is triggered for that component and its children.

Unlike props, the component state is mutable by the component itself (since it owns its state), but mutations have to be done in a specific way; otherwise, React will not detect the changes and won't re-render the component sub-tree. Don't worry though: defining and managing the internal state of a React component is really simple to do.

Let's see how it works!

For now, we'll only show you how to manage state inside of class components. We will explain how to do the same with function components when we introduce React hooks.

For class components, we do the following:

  1. First, we need to initialize the internal state object, either through the constructor function or using a property initialized at creation time. This is the only place where you are allowed to mutate the state directly.
  2. After the object has been constructed, we must use the this.setState(...​) function whenever we want to mutate the state.

Here is an example:

import React from 'react'; 
 
export class Calculator extends React.Component { 
    constructor(props) { 
        super(props); 
        this.state = { 
            currentResult: this.props.initialValue, 
        }; 
    } 
 
    render() { 
        return ( 
            <div> 
                <span>Current result: {this.state.currentResult}</span> 
                <br/> 
                <br/> 
                <button onClick={() => this.add(1)}>+1</button> 
            </div> 
        ); 
    } 
 
    add(value) { 
        this.setState((state, props) => { 
            return {currentResult: state.currentResult + value} 
        }); 
    } 
} 

In the add(...​) function of this example, you can see how the state of the component is updated. It is not the only way to do it, but it is the safest, and the recommended, approach.

You can find the source code of this example in the code samples of this book, under Chapter11/04-react-component-state.

As stated before, the class constructor and initializers are the only places that are allowed to directly set the state of a class component. After construction time, the this.setState({...​}) method needs to be called instead. Otherwise, React won't know about the change and won't re-render the component.

State updates made through calls to setState(...​) are merged shallowly into the current state (that is, only what you define in the new object is replaced in the previous one).

One thing to be careful about is that props and state updates can happen asynchronously (for example, for performance reasons, React might decide to regroup some updates and perform them all at once). For this reason, we had to use the second form of the setState method, which accepts a function as an argument. As you can see in the preceding example, that function receives state (the first argument) and props (the second argument).

Components can pass their own state to children components (for example, as props), but they remain in charge of those values. By default, React will re-render a component and its children anytime state or props change, even if those changes don't impact what should be rendered! You can avoid wasteful re-renders using the shouldComponentUpdate method: https://reactjs.org/docs/react-component.html#shouldcomponentupdate.

Just like with Angular and Vue.js, you should try to maximize the number of dumb components in your applications so as to favor code reuse. The official documentation of React provides specific guidance for this: https://reactjs.org/docs/lifting-state-up.html. You should also take a look at the following page, which shares great ideas about how to develop modern component-based web applications: https://reactjs.org/docs/thinking-in-react.html.

There's more to know, but we can't cover all of the details here; check out the resources in the Further reading section of this chapter if you want to learn more.

To conclude, defining and managing state in a class component is dead simple.

We will soon take a look at React hooks and the useState one in particular, which will allow us to create stateful function components. But first, let's learn about something else: lifecycle hooks!