Understanding State in React JS - Part 4
Tags: webdev, react, javascript
Published: 2021-07-06 13:53:36

Hello everyone 👋,

In the previous article of Learn React JS Series, we learned about,
1. What is a Component?
2. When to use a Component?
3. How to create a Component?
4. How to separate a big component into smaller components?

In this Part 4 of **Learn React JS Series**, we will learn about what is **State**, how to use it, and when to use it.

### What is State?

State is similar to props, but it is private and fully controlled by the component.

### When to use State?

In the previous part of this series, we created a **SearchResults** functional component that accepts props as an argument and renders the URL, title, and description. 

```jsx
function SearchResult(props) {
  return (
    <div>
      <div className="search-url">{props.url}</div>
      <h2 className="search-title">{props.title}</h2>
      <div className="search-description">{props.description}</div>
    </div>
  );
}
export default SearchResult;
```

Assume that you wanted to update the URL to **localhost** on the click event of a button.

```jsx
function SearchResult(props) {
  // newly added - handler for button click
  function updateURL() {
    props.url = "localhost";
  }

  return (
    <div>
      <div className="search-url">{props.url}</div>
      <h2 className="search-title">{props.title}</h2>
      <div className="search-description">{props.description}</div>
       // newly added
      <button onClick={updateURL}>Update URL</button>
    </div>
  );
}
export default SearchResult;
```

When the click event happens on the `button`, `updateURL` function is triggered to update the URL in `props`. But, when it tries to update the URL, the following error will be displayed.

![Screenshot 2021-07-06 at 5.36.06 PM.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1625573202619/lX2elNX1x.png)

This is because `props` are the read-only property and the props values are obtained from the parent component. Props cannot be directly updated in the Component.

Similarly, taking a Counter example, if we have a **Counter** component, the counter value should be controlled by the Counter component. In this case, we should use **State** instead of **Props** to maintain the state for each component on its own. Let's see it in the next section on how to use it.

### How to use State?


- Create a class Component with for `Counter` and extend the `React.Component`. 

```jsx
import React from "react";

export class Counter extends React.Component {

}
```
- Override the constructor and pass `props` to the base class.

```jsx
import React from "react";

export class Counter extends React.Component {

constructor(props) {
    super(props);
  }

}

```
- Define the state with its initial values. In our case, `count` is initialized to 0 and `isStarted` as false. `isStarted` flag is used to toggle the label. (start/stop)

```jsx
export class Counter extends React.Component {

constructor(props) {
    super(props);
    this.state = { count: 0, isStarted: false };
  }

}
``` 
- Override the `render` method. Render() method should return a value JSX. In the render() method, we have a button that displays either Stop/Start based on `isStarted` flag from the state object & `span` tag to show counter value.

```jsx
export class Counter extends React.Component {

constructor(props) {
    super(props);
    this.state = { count: 0, isStarted: false };
  }

  render() {
    return (
      <div className="counter">
        <button className="btn">
          {this.state.isStarted ? "Stop" : "Start"}
        </button>

        <span>Count is {this.state.count}</span>
      </div>
    );
  }

}
```

- To start the counter on the `button` click, listen to the `onClick` event on the button with the handler function.

```jsx
export class Counter extends React.Component {

constructor(props) {
    super(props);
    this.state = { count: 0, isStarted: false };
    // This binding is necessary to make `this` work in the callback. eg (toggleButton)
    this.toggleButton = this.toggleButton.bind(this);
  }

 toggleButton() {
    if (!this.state.isStarted) {
     // clicked Start button, so start the timer
    } else {
    // clicked stopped button, so clear the timer
    } 
  }Ï

  render() {
    return (
      <div className="counter">
        <button className="btn" onClick={this.toggleButton}>
          {this.state.isStarted ? "Stop" : "Start"}
        </button>

        <span>Count is {this.state.count}</span>
      </div>
    );
  }

}

```

To increment the counter, we should use `this.setState` instead of directly changing the counter by this.state.counter = this.state.counter + 1.  
Read more on why [States should not be directly modified](https://reactjs.org/docs/state-and-lifecycle.html#do-not-modify-state-directly)

There are 2 ways to update the State.

1. `this.setState({})` accepts an object to update the state of the component with key-value pair. Eg: `this.setState({count: this.state.count})`

2. `this.setState()` also accepts a function rather than an object with the previous state as the first argument, and the props at the time the update is applied as the second argument. 

As state update are asynchronous, it is better to use this way whenever the previous state is used to calculate new values. Syntax : `this.setState((state, props) => { } )` 

In our case, we can update the count state by,

```jsx
 this.setState((state) => ({
       count: state.count + 1,
 }));
```

Our final code for the Counter component,

```jsx
import React from "react";

export class Counter extends React.Component {
  constructor(props) {
    super(props);
     // local state
    this.state = { count: 0, isStarted: false };
    // This binding is necessary to make `this` work in the callback. eg (toggleButton)
    this.toggleButton = this.toggleButton.bind(this);
  }

  toggleButton() {
    if (!this.state.isStarted) {
       // clicked Start button, so start the timer
      this.counterInterval = setInterval(() => {
        // Update the counter state
        this.setState((state) => ({
          count: state.count + 1,
        }));
      }, 1000);
    } else {
       // clicked stopped button, so clear the timer
      clearInterval(this.counterInterval);
    }
    // update the isStarted state
    this.setState({
      isStarted: !this.state.isStarted,
    });
  }

  render() {
    return (
      <div className="counter">
        <button className="btn" onClick={this.toggleButton}>
          {this.state.isStarted ? "Stop" : "Start"}
        </button>

        <span>Count is {this.state.count}</span>
      </div>
    );
  }
}
```

Use the counter component in the `App.js`

```jsx
import React from "react";
import { Counter } from "./Counter";

function App(props) {
  return (
    <div className="container">
      <h1>Understanding State</h1>
      <Counter></Counter>
      <Counter></Counter>
    </div>
  );
}

export default App;
```

### Output

We have used the Counter component 2 times. You can clearly see the counter state is maintained by its own Counter component instance. The count value is different as we started the 2nd one after some seconds.

![counter.gif](https://cdn.hashnode.com/res/hashnode/image/upload/v1625577985340/Ua1MD3gwR.gif)

### Resource

Here's my [Github](https://github.com/yuvgeek/learn-react/tree/part-4) repo where you can find all the files in the part-4 branch. You can clone and try it out!


Thanks for reading the article. I hope you like it!

You can connect with me on [Twitter](https://twitter.com/yuvgeek) & [Github](https://github.com/yuvgeek) :)