'React Click Counter: Updating State of just one element

This should be pretty simple, but I can't figure out how to do it.

I have a component with multiple buttons, each with a "count" value, set with state. When a user clicks, the count goes up.

Right now, when I click one of the buttons, both counters change. How can I make it so only the div that was clicked updates, using the same state?

Edit: I don't want to have different counts, as I'd like for this component to render buttons dynamically. What if I don't know how many buttons I'll have at first?

class Block extends React.Component {
  state = {
    count: 0
  };

  handleClick = e => {
    const count = this.state.count;
    this.setState({ count: count + 1 });
  };

  render() {
    return (
      <div>
        <button className="block" onClick={this.handleClick}>
          <div className="counter">{this.state.count}</div>
        </button>
        <button className="block" onClick={this.handleClick}>
          <div className="counter">{this.state.count}</div>
        </button>
      </div>
    );
  }
}


Solution 1:[1]

This is more of an issue of learning how to think in react.

If you need to be able to reuse a piece of functionality like a counter, you can make it its own component and have it manage its own state. Then you can reuse it wherever you need.

Here's an example:

class Counter extends React.Component {
  state = {
    count: 0
  };

  handleClick = () => {
    // Use updater function when new state is derived from old
    this.setState(prev => ({ count: prev.count + 1 }));
  };

  render() {
    return (
      <button className="block" onClick={this.handleClick}>
        <div className="counter">{this.state.count}</div>
      </button>
    );
  }
}

// Now you can use it dynamically like this:
class Block extends React.Component {
  render() {
    return (
      <div>
        <div>There are 4 counter component instances that each manage their own state.</div>
        {[1,2,3,4].map(v => <Counter />)}
      </div>
    );
  }
}

ReactDOM.render(<Block />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Solution 2:[2]

you should define two state and when press each button update the current state and you can render the current state in the dome like this

state = {
  firstCount: 0,
  secondCount: 0
}

and write your action (function) to handle update state like this

handleUpdateCount = stateName => {
  this.setState({
    [stateName]= this.state[stateName] + 1
  })
}

then you should called this function like this =>

this.handleUpdateCount('firstCount')

Solution 3:[3]

If your buttons are dynamic you can set your state to be an array and update the relevant index

class Block extends React.Component {
  state = [];

  handleClick = index => {
    this.setState(state => {
       const newState = [...state]; //keep state immutable
       !newState[index] && (newState[index] = 0)
       newState[index]++

       return newState
    });
  };

  render() {
    return (
      <div>
        {[1,2,3].map((value, index) => <button className="block" onClick={() => this.handleClick(index)}>
          <div className="counter">{this.state[index]}</div>
        </button>)}
      </div>
    );
  }
}

Solution 4:[4]

You have to use another value to update function when new state is derived from old state (like increment)

import React, { Component } from 'react'

export class Ref3 extends Component {
    constructor(props) {
      super(props)
    
      this.state = {
         count:0
      }
    }
    //use prevState to help you update the old value to a new one  
    clickHandler=()=>{
        this.setState((prevState=>({
            count:prevState.count+1
        })))
    }
    
  render() {
    return (
      <div>
        <button onClick={this.clickHandler}>Click To Count</button>
        {this.state.count}
      </div>
    )
  }
}

export default Ref3

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 amir hosein Hesarakii
Solution 3 NirG
Solution 4 Ahmad Abdelfattah