Search code examples
reactjsreact-boilerplate

How to change the state of an item affected by onClick in array?


I'm making a page where I need to make multiple selections of buttons (like a filter, which I'll use for the next page).

the information from these buttons is coming from an array and I'm using .map () to mount the button list.

My problem is how do I change the state of only the button that was clicked. The way it is now, when I click, all the buttons are active.

How can I solve this?

Thank you.

import React from 'react';
import { Link } from 'react-router-dom';
import { FormattedMessage } from 'react-intl';
import messages from './messages';

import { getLevel, getDiscipline } from '../../functions';

import template from './index.pug';

export default class ConfigAssessment extends React.PureComponent { // eslint-disable-line react/prefer-stateless-function

  constructor(props){
    super(props);
    this.state = {
      level: getLevel(),
      discipline: getDiscipline(),
      active: '',
      first_click: true,
    }
  }

  changeActive = () => {
    if (this.state.first_click === true) {
      this.setState({
        active: 'active',
        first_click: false
      });
    } else {
      this.setState({
        active: '',
        first_click: true,
      });
    }
  }


  render() {
    return(
<div className="configuration">
      <div className="config-title">
        <i className="ti-settings" />
        <h2>
          <FormattedMessage {...messages.configAssessment} />
        </h2>
      </div>
      <div className="config-items">
        <div className="form-group">
          <label>
            <FormattedMessage {...messages.level} />
          </label>
          <div className="row">
            {this.state.level.map((level, i) => (
              <div className="col-xs-1 col-md-4 col-lg-3" key={level.id}>
                <button
                  className={`btn btn-light-gray btn-block ${this.state.active}`}
                  id={level.id}
                  onClick={this.changeActive}
                >
                  {level.level}
                </button>
              </div>
              ))}
          </div>
        </div>
        <div className="form-group">
          <label>
            <FormattedMessage {...messages.discipline} />
          </label>
          <div className="row">
            { this.state.discipline.map((discipline, i) => (
              <div className="col-xs-1 col-md-4 col-lg-3" key={i}>
                <button
                  className={`btn btn-light-gray btn-block ${this.state.active}`}
                  onClick={this.changeActive}
                >
                  {discipline.discipline}
                </button>
              </div>
              ))}
          </div>
        </div>
        <div className="form-group">
          <label>
            <FormattedMessage {...messages.selectQuestion} />
          </label>
          <div className="row">
            <div className="col-xs-1 col-md-4 col-lg-3">
              <button
                className={`btn btn-light-gray btn-block ${this.state.active}`}
                onClick={this.changeActive}
              >
                <FormattedMessage {...messages.typeAutomatic} />
              </button>
            </div>
            <div className="col-xs-1 col-md-4 col-lg-3">
              <button
                className={`btn btn-light-gray btn-block ${this.state.active}`}
                onClick={this.changeActive}
              >
                <FormattedMessage {...messages.typeManual} />
              </button>
            </div>
          </div>
        </div>
        <div className="form-group fg-right">
          <Link className="btn btn-warning" to="#">
            <FormattedMessage {...messages.createAssessment} />
          </Link>
        </div>
      </div>
    </div>
  );
  }
}

Solution

  • Create a separate component for button

    class MyButton extends Component {
    constructor(props){
        super(props);
        this.state = {
            person: this.props.person
        }
    }
    
    buttonActiveHandler = () => {
        let oldStatus = this.props.person.status;
        this.props.person.status = (!oldStatus ? 'active': '');
        this.setState({
            person:this.props.person
        });
    }
    render() {
        return (
            <button className={this.state.person.status} onClick={this.buttonActiveHandler}>{this.state.person.name}</button>
        );
    }
    }
    export default MyButton;
    

    Then import button component. use map function to for your code block

    <div className={classes.Box}>
         <h4>Lorem, ipsum.</h4>
        {
           this.props.survey.map((person, i) => {
             return (
                <MyButton key={i} person={person}/>
             )
          })
       }
    </div>