React Click Counter: Updating State of just one element

15,195

Solution 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

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

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>
    );
  }
}
Share:
15,195

Related videos on Youtube

adp
Author by

adp

Updated on June 04, 2022

Comments

  • adp
    adp almost 2 years

    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>
        );
      }
    }
    
    • jonrsharpe
      jonrsharpe about 4 years
      Why did you think only one would change? You need to have two separate counts in that component, or two components each with their own count.
    • adp
      adp about 4 years
      well, that's what I was trying to avoid... because what if my buttons were dynamic and I don't know the right amount of buttons I'll end up with from the beginning? @jonrsharpe
    • jonrsharpe
      jonrsharpe about 4 years
      Have the state be an array, and address them by index? Or, again, have each one manage its own state.
  • Jorge Mauricio
    Jorge Mauricio almost 3 years
    One correction...[stateName]= this.state[stateName] + 1 should be [stateName]: this.state[stateName] + 1 change = to :