0

i want to be able to toggle class "active" on/off on a single item of a list. Say we have a list of elements:

<ul>
  <li><a href="#" className="list-item">First</a></li>
  <li><a href="#" className="list-item">Second</a></li>
  <li><a href="#" className="list-item">Third</a></li>
</ul>

and I want to be able to add class "active" on a second element on click. I cannot use state for that, because it would change the classes of other 2 elements, if we add the condition there, right ?

So the solution could be to create a sub-component say <ListItem /> that could have its own state and own onclick method, that would change only his own class. But then, when we want to remove class "active" on other elements on click, this sub-component would have to dispatch method to a parent to remove class from other elements. It seems pretty complicated for a task of a easy kind ( you know the 10secs-in-jquery kind).

Does anyone have an easier solution for this ?

It would be very nice to not have to use another big npm module for this, document.getElementsByClassName (obviously) nor refs.

1
  • what is the issue with using state here? because it would change the classes of other 2 elements what do you mean by this? you wouldn't have them all change on a state change. and even if they did this wouldn't be a performance issue. Not sure what the problem is. Commented May 14, 2017 at 6:51

2 Answers 2

2

you can manage the selected state outside the item component and pass it as a prop.
An event handler of onSelect / onClick can trigger and change the state.

const data = [1,2,3,4,5];

class List extends React.Component {
	constructor(props){
			super(props);
      
      this.state = {
      	selectedItem: 0
      };
      this.onSelect = this.onSelect.bind(this);
  }
  
  onSelect(id){
  	this.setState({
    	selectedItem: id
    });
  }
  
  render() {
  	const {selectedItem} = this.state;
  	return ( 
      <ul>
          {data.map((d, index) => {
          	return <ListItem 
                      key={index}
                      selected={index + 1  == selectedItem}
                      value={d}
                      id={d}
                      onSelect={this.onSelect}
                    />
          })}
          
      </ul>
    )
  }
}

const ListItem = (props) => {

  const onSelect = (e) => {
  	props.onSelect(e.target.id);
  }
  
  const className = props.selected && "selected";
  	
	return <li className={className} id={props.id} onClick={onSelect}>{props.value}</li>
}

ReactDOM.render(<List />, document.getElementById("root"));
ul{
  list-style: none;
}

li{
    padding: 5px;
    border: 1px solid #ccc;
    cursor: pointer;
}

li.selected{
  background-color: green;
  color: #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>

Sign up to request clarification or add additional context in comments.

Comments

2

The correct way to approach this problem would be to have a state variable that defines which one is currently "active", i.e.

this.state = {
    active: "first" // (or "second", "third", null, etc..) 
};

Then, you can use an inline if statement like so:

<li><a href="#" className={"list-item" + (this.state.active == "first" ? " active": "")}>First</a></li>

Note that I am assuming you want to hardcode these list elements - if these elements are dynamically generated it's a simple matter of setting this.state.active to being the index/ref of the currently selected element.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.