简体   繁体   中英

How do I deselect an active list item when I click on/activate another one?

I got a simple, stateless parent component displaying a list stateful child components. Each individual child component represents a list item whose active state can be toggled (true/false). An active list item becomes green, with close (times) being displayed.

Now, my issue is when one item is selected (active) and I click on another item, both become active, as is expected. However, what I'd like is for the previous item to be deselected/deactivated, such that only one item can be active at a time. I tried lifting state up to the parent and passing it down as props, but this obviously results in every item being active when I click on one item. How do I achieve this?

Below are the code snippets.

import React, { Component } from "react";

class ListItem extends Component {
  state = {
    isActive: false,
  };

  onToggleSelect = () =>
    this.setState({
      isActive: !this.state.isActive,
    });

  render() {
    return (
      <li
        style={{ color: this.state.isActive && "green", cursor: "pointer" }}
        onClick={this.onToggleSelect}
      >
        Item number {this.props.item}
        {this.state.isActive && <span>&times;</span>}
      </li>
    );
  }
}

function List({ itemList }) {
  return (
      <div>
        <ul>
          {
            itemList.map(i => <ListItem key={i} item={i} />)
          }
        </ul>
      </div>
    );
}

List.defaultProps = {
  itemList: [...Array(10).keys()].map(x => x + 1),
};

export default List;

I'd move the click handler up a level:

import React, { useState } from "react";

function ListItem({ item, isActive, handleClick }) {
    return (
      <li
        style={{ color: isActive && "green", cursor: "pointer" }}
        onClick={() => handleClick(item)}
      >
        Item number {item}
        {isActive && <span>&times;</span>}
      </li>
    );
}

function List({ itemList }) {
  const [activeListItem, setActiveListItem] = useState();

  const handleClick = (item) => activeListItem === item ? setActiveListItem() : setActiveListItem(item);

  return (
      <div>
        <ul>
          {
            itemList.map(i => <ListItem key={i} item={i} handleClick={handleClick} isActive={activeListItem === item} />)
          }
        </ul>
      </div>
    );
}

I think you need to move the state in the parent component to better manage the state of each item.

example with class component

import React, { Component } from "react";

class ListItem extends Component {

  render() {
    const isActive = this.props.activeIndex === this.props.item;
    return (
      <li
        style={{ color: isActive && "green", cursor: "pointer" }}
        onClick={() => this.props.onToggleSelect(this.props.item)}
      >
        Item number {this.props.item}
        {isActive && <span>&times;</span>}
      </li>
    );
  }
}

class List extends Component {
  state = {
    activeIndex: null,
  };

  onToggleSelect = (index) =>
    this.setState({
      activeIndex: index,
  });

  return (
      <div>
        <ul>
          {
            this.props.itemList.map(i => (
                <ListItem
                  key={i}
                  activeIndex={this.state.activeIndex}
                  item={i}
                  onToggleSelect={onToggleSelect}
                />
            ))
          }
        </ul>
      </div>
    );
}

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