How to target single item in list with onClick when mapping JSON array in React How to target single item in list with onClick when mapping JSON array in React json json

How to target single item in list with onClick when mapping JSON array in React


Issue

You are using a single boolean value to store a "clicked" state, and all your mapped UI uses that single state to cue from.

Solution

Assuming you would like multiple items to be clicked, and also assuming your mapped data is static (i.e. the faqData isn't added to, removed from, or sorted) then using the mapped index to toggle the "clicked" state is acceptable. use an object to store "clicked" indices and update the handleClick callback to toggle the state. For this use case I like to make the callback a curried handler to enclose in scope the value I wish to use in the callback.

const [clickedIndex, setClickedIndex] = useState({});const handleClick = (index) => () => {  setClickedIndex(state => ({    ...state, // <-- copy previous state    [index]: !state[index] // <-- update value by index key  }));};...<List  style={{    maxHeight: 430,    width: 500,    overflow: 'auto',    border: '1px solid black',  }}>  {faqdata.map((item, index) => (    <ListItem style={{ cursor: 'pointer' }}>      <ListItemIcon>        {clickedIndex[index] ? <AddIcon /> : <RemoveIcon />} // <-- check if index is truthy in clickedIndex state      </ListItemIcon>      <ListItemText        primary={item.Question}        onClick={handleClick(index)} // <-- pass index to handler      />    </ListItem>  ))}</List>


Is this what you're looking for?

import { useState } from "react";import "./styles.css";const faqdata = [  { Question: "Q1", Answer: "A1" },  { Question: "Q2", Answer: "A2" },  { Question: "Q3", Answer: "A3" },  { Question: "Q4", Answer: "A4" }];const AddIcon = () => <span class="icon">&#43;</span>;const RemoveIcon = () => <span class="icon">&#9747;</span>;function ListItem({ d }) {  const [checked, setChecked] = useState(false);  return (    <li      onClick={() => {        setChecked(!checked);      }}    >      {checked ? <RemoveIcon /> : <AddIcon />}      {d.Question}    </li>  );}function List() {  return (    <ul>      {faqdata.map((d) => {        return <ListItem d={d} />;      })}    </ul>  );}

You can try it out here

The problem with the current approach is that there's only one variable to store the added/removed status of every question. So, when the click boolean updates, it updates the state of all elements.

In the code shared above, the ListItem component is responsible for maintaining the added/removed status of each question separately. So, one item in the list can change without affecting the other.


It's one of my test. You should save selected ids and check if the id exists in that array.

    const [selectedfaqdataIds, setSelectedfaqdataIds] = useState([]);    const handleSelect = (event, id) => {     const selectedIndex = selectedfaqdataIds.indexOf(id);     let newselectedfaqdataIds = [];     if (selectedIndex === -1) {        newselectedfaqdataIds = newselectedfaqdataIds.concat(selectedfaqdataIds, id);     } else if (selectedIndex === 0) {        newselectedfaqdataIds = newselectedfaqdataIds.concat(selectedfaqdataIds.slice(1));     } else if (selectedIndex === selectedfaqdataIds.length - 1) {        newselectedfaqdataIds = newselectedfaqdataIds.concat(selectedfaqdataIds.slice(0, -1));     } else if (selectedIndex > 0) {        newselectedfaqdataIds = newselectedfaqdataIds.concat(        selectedfaqdataIds.slice(0, selectedIndex),        selectedfaqdataIds.slice(selectedIndex + 1)      );     }     setSelectedfaqdataIds(newselectedfaqdataIds);      };    {faqdatas.map((faqdata) => (       <ListItem style={{ cursor: 'pointer' }}>            <ListItemIcon>              {selectedfaqdataIds.indexOf(faqdata.id) !== -1}? <AddIcon /> : <RemoveIcon />}            </ListItemIcon>            <ListItemText primary={faqdata.Question} onClick={(event) => handleSelect(event, faqdata.id)} />       </ListItem>    ))}