3

I want to create a country list that the ListItems in it are clickable and each ListItem has an icon near to text when clicked. I created a list and I implemented the clickable property of lists. But, when I click one list item in the list, I cannot select the other item in 1 click, it first deselects the previous one and then selects the other. This is the part of the code that my implementation is included:

class CountryList extends Component {
  constructor(props) {
    super(props);
    this.state = {clicked: false};
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(state => ({
      clicked: !state.clicked
    }));
  }

  render() {
    const {classes} = this.props;
    const { clicked } = this.state;

    let listItems = myList.map((item) =>
      <>
        <ListItem className={ clicked ? classes.listItemClicked : classes.listItemNotClicked }
          onClick={this.handleClick} classes={classes.ListItem} button key={item.id}>
        
            {clicked ? 
            <ListItemIcon><DoneIcon style={{ color: 'rgb(239, 239, 239)', fontSize: '2.2rem' }}/></ListItemIcon>
           : <ListItemIcon><DoneIcon style={{ display: 'none', color: 'rgb(239, 239, 239)', fontSize: '2.2rem', backgroundColor: 'rgb(239, 239, 239)' }}/></ListItemIcon>}

           <ListItemText classes={{primary: classes.listItemText}} primary={item.value} />
       </ListItem>
       <Divider /> 
      </>
    );

    return (
      <List className={classes.list}>{listItems}</List>
    );
  }
}


const myList = [
  {id: 1, value: 'Albania'}, {id: 2, value: 'Austria'}, {id: 3, value: 'Belarus'}, {id: 4, value: 'Belgium'}, 
  {id: 5, value: 'Bosnia'}, {id: 6, value: 'Bulgaria'}, {id: 7, value: 'Croatia'}, {id: 8, value: 'Cyprus'}, 
  {id: 9, value: 'Czech'}, {id: 10, value: 'Denmark'}, {id: 11, value: 'Estonia'}, {id: 12, value: 'Finland'}, 
  {id: 13, value: 'France'}, {id: 14, value: 'Germany'}, {id: 15, value: 'Greece'}, {id: 16, value: 'Hungary'}, 
  {id: 17, value: 'Iceland'}, {id: 18, value: 'Ireland'}, {id: 19, value: 'Italy'}, {id: 20, value: 'Latvia'}, 
  {id: 21, value: 'Lithuania'}, {id: 22, value: 'Luxembourg'}, {id: 23, value: 'Macedonia'}, 
  {id: 24, value: 'Malta'}, {id: 25, value: 'Moldova'}, {id: 26, value: 'Montenegro'}, 
  {id: 27, value: 'Netherlands'}, {id: 28, value: 'Norway'}, {id: 29, value: 'Pakistan'}, 
  {id: 30, value: 'Poland'}, {id: 31, value: 'Portugal'}, {id: 32, value: 'Romania'}, {id: 33, value: 'Russia'}, 
  {id: 34, value: 'Serbia'}, {id: 35, value: 'Slovakia'}, {id: 36, value: 'Slovenia'}, 
  {id: 37, value: 'Spain'}, {id: 38, value: 'Sweden'}, {id: 39, value: 'Switzerland'}, {id: 40, value: 'Turkey'}, 
  {id: 41, value: 'Ukraine'}, {id: 42, value: 'Others'}
];

This is the code in App.js file:

<CountryList myList={myList} />

Also, I added an icon before the text. When I click 1 item, it shows the icon but, it also shows another element icon if my mouse is on it. I want to show the icon of the clicked element. How can I achieve this?

This is the screenshot:

enter image description here

If you look at carefully, there is an icon next to Belgium.

This is the link for the code in codesandbox: https://codesandbox.io/s/gifted-snow-5p1gd?file=/src/Country.js

1
  • 4
    You should create a minimal reproducible example (preferably on codesandbox) Commented Aug 28, 2020 at 15:35

2 Answers 2

7

Here is a working demo

You need to keep a active list item in a state, as i see in your code its this.state.clicked which is a boolean, so now you don't have a way to set the styles of clicked element because there is no reference to which is selected

set a state to keep the clicked item

this.state = { clickedItem: "" };

change onClick to.

<ListItem
    key={item.id}
    className={
      this.state.clickedItem === item.id
        ? classes.listItemClicked
        : classes.listItemNotClicked
    }
    onClick={() => this.handleClick(item)}
>

in onCLick set the clicked items to the state

handleClick(item) {
  this.setState({
    clickedItem: item.id
  });
}

Now you have the clicked item in the state, in the next render (which will be happen due to above state change) you can have the below logic in the render

<ListItem
  key={item.id}
  className={
    this.state.clickedItem === item.id //if item.id === clickedItemId then add a separate css class
      ? classes.listItemClicked
      : classes.listItemNotClicked
  }
  onClick={() => this.handleClick(item)}
>
  <ListItemIcon>
    //if item.id === clickedItemId then show the DoneIcon
    {this.state.clickedItem === item.id && (
      <DoneIcon
        style={{ color: "rgb(239, 239, 239)", fontSize: "2.2rem" }}
      />
    )}
  </ListItemIcon>
  <ListItemText primary={item.value} />
</ListItem>
Sign up to request clarification or add additional context in comments.

1 Comment

thank you for your response. I missed the parameters of handleClick method. Your solution works as I want. It is interesting that one little detail in the code changes the result a lot!
0

You have to map clicked with index. Try this:

class CountryList extends Component {
  constructor(props) {
    super(props);
    this.state = {clicked: null};
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(key) {
    this.setState(state => ({
      clicked: key
    }));
  }

  render() {
    const {classes} = this.props;
    const { clicked } = this.state;

    let listItems = myList.map((item, index) =>
      <>
        <ListItem className={ clicked == index ? classes.listItemClicked : classes.listItemNotClicked }
          onClick={this.handleClick} classes={classes.ListItem} button key={item.id}>
        
            {clicked == index ? 
            <ListItemIcon><DoneIcon style={{ color: 'rgb(239, 239, 239)', fontSize: '2.2rem' }}/></ListItemIcon>
           : <ListItemIcon><DoneIcon style={{ display: 'none', color: 'rgb(239, 239, 239)', fontSize: '2.2rem', backgroundColor: 'rgb(239, 239, 239)' }}/></ListItemIcon>}

           <ListItemText classes={{primary: classes.listItemText}} primary={item.value} />
       </ListItem>
       <Divider /> 
      </>
    );

    return (
      <List className={classes.list}>{listItems}</List>
    );
  }
}


const myList = [
  {id: 1, value: 'Albania'}, {id: 2, value: 'Austria'}, {id: 3, value: 'Belarus'}, {id: 4, value: 'Belgium'}, 
  {id: 5, value: 'Bosnia'}, {id: 6, value: 'Bulgaria'}, {id: 7, value: 'Croatia'}, {id: 8, value: 'Cyprus'}, 
  {id: 9, value: 'Czech'}, {id: 10, value: 'Denmark'}, {id: 11, value: 'Estonia'}, {id: 12, value: 'Finland'}, 
  {id: 13, value: 'France'}, {id: 14, value: 'Germany'}, {id: 15, value: 'Greece'}, {id: 16, value: 'Hungary'}, 
  {id: 17, value: 'Iceland'}, {id: 18, value: 'Ireland'}, {id: 19, value: 'Italy'}, {id: 20, value: 'Latvia'}, 
  {id: 21, value: 'Lithuania'}, {id: 22, value: 'Luxembourg'}, {id: 23, value: 'Macedonia'}, 
  {id: 24, value: 'Malta'}, {id: 25, value: 'Moldova'}, {id: 26, value: 'Montenegro'}, 
  {id: 27, value: 'Netherlands'}, {id: 28, value: 'Norway'}, {id: 29, value: 'Pakistan'}, 
  {id: 30, value: 'Poland'}, {id: 31, value: 'Portugal'}, {id: 32, value: 'Romania'}, {id: 33, value: 'Russia'}, 
  {id: 34, value: 'Serbia'}, {id: 35, value: 'Slovakia'}, {id: 36, value: 'Slovenia'}, 
  {id: 37, value: 'Spain'}, {id: 38, value: 'Sweden'}, {id: 39, value: 'Switzerland'}, {id: 40, value: 'Turkey'}, 
  {id: 41, value: 'Ukraine'}, {id: 42, value: 'Others'}
];

1 Comment

Thank you for your response. When run your code, even if I select an element, its color does not change. I think there is a missing in the code. I will try to edit your code.

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.