React

React state management

state

We introduce the concept of state and life cycle in a React component. Let’s start with a stop watch example.

We will make a stopwatch component which is truly reusable and encapsulated. This is what we want to achieve.

ReactDOM.render(
  <StopWatch/>,
  document.getElementById('root')
);

Instead of using a prop, we are using a state. (this.state.duration). Difference between prop and state is that prop is immutable (once assigned, it can’t be changed in the component) whereas state can change.

One of the most important aspects in javascript applications is that we have to free up resources when they are destroyed/not used anymore. This is where we need to override two functions of React.Component class. The functions are componentDidMount and componentWillUnmount. The componentDidMount function is called when component output has been rendered to the DOM. We shall setup a ticker in this method.

Any tear down activity is to be done in componentWillUnmount function. It is called before component unmounts/gets destroyed. We shall clear the ticker in this method.

This ticker method itself needs to be implemented so that it can change the duration and then reset the state by calling this.setState(). Note that only through setState method you can communicate to component that state has changed so that it can trigger the render method again to show your changes in UI.

All this can be seen in the code below.

class StopWatch extends React.Component {
  
  constructor(props) {
    super(props);
    this.state = {duration: 0};
    this.initDate = new Date();
  }
  
  componentDidMount() {
     this.timerID = setInterval(
      () => this.ticker(),
      1000
    );
  }
  
  ticker() {
    var diff = Math.abs(new Date() - this.initDate);
    var intdiff = Math.round(diff/1000);
    this.setState({
      duration: intdiff
    });
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  render() {
    return (
      <div>
        <h1>Stop Watch!</h1>
        <h2>{this.state.duration} Seconds.           </h2>
      </div>
    );
  }
}

ReactDOM.render(
  <StopWatch />,
  document.getElementById('root')
);

You can see it live in action here.

https://codepen.io/anilpank/pen/QWLzoXZ

Few very important points regarding React State

You must not modify React state directly.

Below is wrong since it will not trigger component render and you will not see your changes in Ui.


this.state.duration = 20;

Below is the correct way to achieve your desired outcome. Always call the setState method.

this.setState({duration : 20});

There is one exception to this. You can set the state directly in your constructor since constructor is called before render method. So below is correct.

class Chart extends React.Component {
  constructor(props) {
   super(props);
   this.state = {duration : 20};
  }
  
  render() {
    return (<h3>The duration is {this.state.duration}</h3>);
  }
  
}

States and props get updated asynchronously

Not knowing this can lead to unexpected behavior and buggy code. So why does React behave like this. According to documentation it merges multiple setState calls to one in order to improve performance.

So due to this, this might not work correctly if this setState call is merged with other setState calls.

// Wrong
this.setState({
  balance: this.state.balance+ this.props.deposit,
});

There are two correct ways of doing it. First one is very verbose one. Basically taking the operation outside the setState method and then calling setState method.

let newBalance = this.state.balance + this.props.deposit;
this.setState({
  balance: newBalance ,
});

The second way is using the second form of setState method which accepts a function as argument.

this.setState(
  function (state, props) {
      return {balance : state.balance + props.deposit};
  }
);

This can be condensed further using arrow function.

this.setState((state,props) => {balance : state.balance + props.deposit});

State updates don’t create new objects, instead merge into existing state object

Your state may contain several objects.

constructor(props) {
    super(props);
    this.state = {
      books: [],
      authors: []
    };
  }

Then you update them independently in separate setState calls.

 componentDidMount() {
    fetchBooks().then(response => {
      this.setState({
        books: response.books
      });
    });

    fetchAuthors().then(response => {
      this.setState({
        authors: response.authors
      });
    });
  }

The merging is shallow, so this.setState({books}) leaves this.state.authors intact, but completely replaces this.state.books.

The data always flows down.

This is very crucial for understanding React. No component can know if it’s parent component or child component is stateful or stateless. (A stateful component is a component that has state where a stateless component will only have props).

A component can pass it’s state down as props to it’s child component.

<Room noOfDoors={this.state.doorCount} />

function Room(props) {
   return (<h3>This room as {props.noOfDoors} doors</h3> )
}

This is called top down data flow. A component’s state can only be passed down, that is to it’s child components as props. By design a stateless component can be inside a stateful component and vice versa.

How useful was this post?

Click on a star to rate it!

Average rating / 5. Vote count:

As you found this post useful...

Follow us on social media!

We are sorry that this post was not useful for you!

Let us improve this post!

About anil

Hi, I'm Anil Verma. I have a passion for teaching and developing awesome front end apps. I was involved in development of post processor for Nastran(built by NASA). More recently I have been building supply chain apps at E2open.
View all posts by anil →

Leave a Reply

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