在 React 中映射 JSON 数组时如何使用 onClick 定位列表中的单个项目

Posted

技术标签:

【中文标题】在 React 中映射 JSON 数组时如何使用 onClick 定位列表中的单个项目【英文标题】:How to target single item in list with onClick when mapping JSON array in React 【发布时间】:2021-05-31 15:21:02 【问题描述】:

我有一个包含对象数组的 JSON 文件,每个对象由“问题”和“答案”组成(我正在创建一个常见问题解答部分)。我正在做的是映射数组并显示问题列表,效果很好。 Next to each question is an icon 并且我希望在单击它时更改图标,但它正在更改列表中的 EVERY icon 而不仅仅是单击的那个项目。

我正在使用 Material UI 和钩子,这就是我设置 handleClick 的方式:

const [click, setClick] = useState(true);
const handleClick = () => 
   setClick(!click);
;

这就是我设置数组映射的方式:

<List
   style=
     maxHeight: 430,
     width: 500,
     overflow: 'auto',
     border: '1px solid black',
        
      >
        faqdata.map((item) => (
          <ListItem style= cursor: 'pointer' >
            <ListItemIcon>
              click ? <AddIcon /> : <RemoveIcon />
            </ListItemIcon>
            <ListItemText primary=item.Question onClick=handleClick />
          </ListItem>
        ))
      </List>

如何才能使图标仅在我单击的列表项而不是列表中的每个列表项上发生变化?我的 onClick 在不正确的位置吗?任何帮助将不胜感激。谢谢!

【问题讨论】:

一次可以“点击”多个项目还是只“点击”一个?您需要存储被点击项目的 id,而不仅仅是一个真/假值。如果可以单击多个项目,我将创建一个呈现单个项目并在该组件中保持布尔状态的组件。每个项目都必须有一个单独的布尔值,而不仅仅是一个。 【参考方案1】:

这是你要找的吗?

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>
  );

你可以试试here

当前方法的问题是只有一个变量来存储每个问题的添加/删除状态。因此,当 click 布尔值更新时,它会更新所有元素的状态。

在上面分享的代码中,ListItem组件负责分别维护每个问题的添加/删除状态。因此,列表中的一项可以更改而不会影响另一项。

【讨论】:

【参考方案2】:

这是我的测试之一。您应该保存选定的 id 并检查该 id 是否存在于该数组中。

    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>
    ))

【讨论】:

【参考方案3】:

问题

您正在使用单个布尔值来存储“点击”状态,并且所有映射的 UI 都使用该单个状态来进行提示。

解决方案

假设您希望单击多个项目,并假设您的映射数据是静态的(即 faqData 未添加、删除或排序),然后使用映射索引切换“已单击”状态是可以接受的。使用对象存储“点击”索引并更新handleClick 回调以切换状态。对于这个用例,我喜欢将回调设置为 curried 处理程序,以将我希望在回调中使用的值包含在范围内。

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>

【讨论】:

以上是关于在 React 中映射 JSON 数组时如何使用 onClick 定位列表中的单个项目的主要内容,如果未能解决你的问题,请参考以下文章

如何在 React 中显示来自 JSON URL 数组的图像

使用 React 映射图像数组时如何创建图像背景渐变

映射数组时如何为 React 子级指定键

React:处理映射状态

如何在 React 和 Typescript 中映射我的 Union 类型的 GraphQL 响应数组

如何从 JSON 映射嵌套数组?