gamer gamer - 4 months ago 78
Javascript Question

react change class name on state change

I have a state like this where I am setting

active
and
class
flag like this:

constructor(props) {
super(props);
this.state = {'active': false, 'class': 'album'};
}

handleClick(id) {
if(this.state.active){
this.setState({'active': false,'class': 'album'})
}else{
this.setState({'active': true,'class': 'active'})
}
}


And I have a list of items with class name from state:

<div className={this.state.class} key={data.id} onClick={this.handleClick.bind(this.data.id}>
<p>{data.name}</p>
</div>


Here how can I change the class name of particular div?

Answer

Below is a fully functional example of what I believe you're trying to do (with a functional snippet).

Explanation

Based on your question, you seem to be modifying 1 property in state for all of your elements. That's why when you click on one, all of them are being changed.

In particular, notice that the state tracks an index of which element is active. When MyClickable is clicked, it tells the Container its index, Container updates the state, and subsequently the isActive property of the appropriate MyClickables.

Example

var Container = React.createClass({
  getInitialState() {
    return {
      activeIndex: null
    };
  },

  handleClick(index) {
    this.setState({activeIndex: index})
  },

  render() {
    return <div>
      <MyClickable name="a" index={0} isActive={this.state.activeIndex===0} onClick={this.handleClick}/>
      <MyClickable name="b" index={1} isActive={this.state.activeIndex===1} onClick={this.handleClick}/>
      <MyClickable name="c" index={2} isActive={this.state.activeIndex===2} onClick={this.handleClick}/>
    </div>
  }
})

var MyClickable = React.createClass({

  handleClick() {
    this.props.onClick(this.props.index)
  },

  render () {
    return <button type='button' className={this.props.isActive ? 'active' : 'album'} onClick={this.handleClick}>
      <span>{this.props.name}</span>
    </button>
  }
})

ReactDOM.render(<Container />, document.getElementById('app'))

Demo

"use strict";

var Container = React.createClass({
  displayName: "Container",

  getInitialState: function getInitialState() {
    return {
      activeIndex: null
    };
  },

  handleClick: function handleClick(index) {
    this.setState({
      activeIndex: index
    });
  },

  render: function render() {
    return React.createElement(
      "div",
      null,
      React.createElement(MyClickable, {
        name: "a",
        index: 0,
        isActive: this.state.activeIndex === 0,
        onClick: this.handleClick
      }),
      React.createElement(MyClickable, {
        name: "b",
        index: 1,
        isActive: this.state.activeIndex === 1,
        onClick: this.handleClick
      }),
      React.createElement(MyClickable, {
        name: "c",
        index: 2,
        isActive: this.state.activeIndex === 2,
        onClick: this.handleClick
      })
    );
  }
});

var MyClickable = React.createClass({
  displayName: "MyClickable",

  handleClick: function handleClick() {
    this.props.onClick(this.props.index);
  },

  render: function render() {
    return React.createElement(
      "button", {
        type: "button",
        className: this.props.isActive ? 'active' : 'album',
        onClick: this.handleClick
      },
      React.createElement(
        "span",
        null,
        this.props.name
      )
    );
  }
});

ReactDOM.render(React.createElement(Container, null), document.getElementById('app'));
button {
  display: block;
  margin-bottom: 1em;
}
.album > span:after {
  content: ' (an album)';
}
.active {
  font-weight: bold;
}
.active > span:after {
  content: ' ACTIVE';
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.1/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.2.1/react-dom.min.js"></script>
<div id="app"></div>

Update: "Loops"

In response to a comment about a "loop" version, I believe the question is about rendering an array of MyClickable elements. We won't use a loop, but map, which is typical in React + JSX. The following should give you the same result as above, but it works with an array of elements.

// New render method for `Container`
render() {

  var clickables = [
      { name: "a" },
      { name: "b" },
      { name: "c" },
  ]

  return <div>
      { clickables.map(function(clickable, i) {
          return <MyClickable
            name={ clickable.name }
            index={ i }
            isActive={ this.state.activeIndex === i }
            onClick={ this.handleClick }
          /> } )
      }
  </div>
}