简体   繁体   中英

Implement 'toggle class' functionality on a list of elements with React

I'm struggling with implementing 'toggle class' functionality in React. I have the NavSidebar component which is an unordered list ul . My NavListItem component is list item li .

When the list item is clicked I need to give the class name active to the clicked li and other list elements should get an empty class name.

Here's what I've come up with by far:

class NavListItem extends React.Component {
  constructor(props) {
    super(props);
    this.state = { active: false };
  }

  setActive(bool) {
    this.setState({ active: bool });
  }

  render() {
    let index = this.props.key;
    let item = this.props.item;
    return (
      <li className={this.state.active ? 'active' : ''}
          onClick={this.setActive(this.props.setActive).bind(this)} >
        <a href={'#'+item}>{item}</a>
      </li>
    );
  }
}

class NavSidebar extends React.Component {

  constructor(props) {
    super(props);
  }

  render () {

    let items = this.props.navItems.map( (item, index) => {
      return (
        <NavListItem key={index} item={item} setActive={true} />
      );
    });

    return (
        <div className="col-sm-2">
          <nav className="nav-sidebar">
            <ul className="nav">
              {items}
              <li className="nav-divider" />
              <li>
                <a href="#"><i className="glyphicon glyphicon-home"></i> Back Home</a>
              </li>
            </ul>
          </nav>
        </div>
    );
  }
}

NavSidebar.propTypes = {
  navItems: React.PropTypes.array
};

Unfortunately, this doesn't work, it never even touches the className of the elements and I can't come up with the working solution.

Could you please help me with that?

There is no error message here?

onClick={this.setActive(this.props.setActive).bind(this)}

Because you are binding undefined , result of calling this.setActive(this.props.setActive)

and even if you remove the bind, it will not work as the click handler will be undefined . Also, the state should never be modified inside render .

In any case, because you want to update more than one item (the new selected one and the old one), the parent should be responsible of this and should:

  • activate the clicked item
  • deactivate all others
  • re-render the list

Something like this (not tested):

class NavListItem extends React.Component {
  constructor(props) {
    super(props);
  }

  setActive(bool) {
    this.props.onItemActive(this.props.item);
  }

  render() {
    let item = this.props.item;
    return (
      <li className={this.props.active ? 'active' : ''}
          onClick={this.setActive.bind(this)} >
        <a href={'#'+item}>{item}</a>
      </li>
    );
  }
}

class NavSidebar extends React.Component {

  constructor(props) {
    super(props);
    this.state = {activeItem: null};
  }

  onItemActive(item) {
    this.setState({activeItem: item};
  }

  render () {
    let self = this;
    let items = this.props.navItems.map( (item, index) => {
      return (
        <NavListItem key={index} item={item} 
                     onItemActive={self.onItemActive.bind(self)} 
                     active={item === self.state.activeItem} />
      );
    });

    return (
        <div className="col-sm-2">
          <nav className="nav-sidebar">
            <ul className="nav">
              {items}
              <li className="nav-divider" />
              <li>
                <a href="#"><i className="glyphicon glyphicon-home"></i> Back Home</a>
              </li>
            </ul>
          </nav>
        </div>
    );
  }
}

NavSidebar.propTypes = {
  navItems: React.PropTypes.array
};

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM