You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React useState中实现各类数组方法(手风琴场景)

React手风琴组件:基于useState的展开状态数组操作方案

场景与需求

开发手风琴组件,使用如下数据结构:

{
    "options": [
        {
            "id": 1,
            "name": "HELP & INFORMATION",
            "option": [
                "Help",
                "Track order",
                "Delivery & returns"
            ]
        },
        {
            "id": 2,
            "name": "ABOUT SLEORPELS",
            "option": [
                "About us",
                "Career at SLEORPELS",
                "Corporate responsibility",
                "Investors' site"
            ]
        },
        {
            "id": 3,
            "name": "MORE FROM SLEORPELS",
            "option": [
                "Mobile and SLEORPELS apps",
                "SLEORPELS Marketplace",
                "Gift vouchers",
                "Black Friday",
                "SLEORPELS x Thrift+"
            ]
        }
    ]
}

需求:点击选项标题(options.name)时展开/收起对应选项列表(options.option),通过useState维护存储展开项id的数组,以此控制展开状态。

核心解决思路

React中useState的状态是不可变的,不能直接对状态数组使用push/splice等修改原数组的方法,必须通过生成新数组来更新状态,触发组件重渲染。以下是对应数组操作的替代实现:

1. 判断id是否已存在(替代includes/indexOf)

直接读取当前状态数组,调用原生includes方法即可:

const isExpanded = expandedIds.includes(item.id);

2. 添加id到数组(替代push)

使用扩展运算符(...)复制原数组,并追加新id,生成新数组后更新状态:

setExpandedIds(prevIds => [...prevIds, id]);

3. 从数组移除id(替代splice/pop)

使用filter方法生成不包含目标id的新数组:

setExpandedIds(prevIds => prevIds.filter(itemId => itemId !== id));

完整组件示例

import { useState } from 'react';

const Accordion = () => {
  // 初始化展开状态数组,为空表示所有项默认收起
  const [expandedIds, setExpandedIds] = useState([]);
  
  // 手风琴数据
  const accordionData = {
    "options": [
      {
        "id": 1,
        "name": "HELP & INFORMATION",
        "option": ["Help", "Track order", "Delivery & returns"]
      },
      {
        "id": 2,
        "name": "ABOUT SLEORPELS",
        "option": ["About us", "Career at SLEORPELS", "Corporate responsibility", "Investors' site"]
      },
      {
        "id": 3,
        "name": "MORE FROM SLEORPELS",
        "option": ["Mobile and SLEORPELS apps", "SLEORPELS Marketplace", "Gift vouchers", "Black Friday", "SLEORPELS x Thrift+"]
      }
    ]
  };

  // 点击标题切换展开状态
  const toggleExpand = (id) => {
    setExpandedIds(prevIds => {
      if (prevIds.includes(id)) {
        // 已展开则移除id,收起
        return prevIds.filter(itemId => itemId !== id);
      } else {
        // 未展开则添加id,展开
        return [...prevIds, id];
      }
    });
  };

  return (
    <div className="accordion">
      {accordionData.options.map(item => (
        <div key={item.id} className="accordion-item">
          {/* 标题按钮 */}
          <button 
            className="accordion-title"
            onClick={() => toggleExpand(item.id)}
          >
            {item.name}
            <span>{expandedIds.includes(item.id) ? '▼' : '▶'}</span>
          </button>
          {/* 展开内容,根据状态控制显示 */}
          {expandedIds.includes(item.id) && (
            <ul className="accordion-content">
              {item.option.map((option, idx) => (
                <li key={idx}>{option}</li>
              ))}
            </ul>
          )}
        </div>
      ))}
    </div>
  );
};

export default Accordion;

额外说明

  • 使用函数式更新(setExpandedIds(prevIds => ...))可以确保每次更新都基于最新的状态值,避免因闭包导致的状态不一致问题。
  • 如果需要支持同时展开多个项,当前方案完全适用;如果需要仅展开单个项,只需将状态改为存储单个id(而非数组),点击时直接设置为当前id或null即可。

内容的提问来源于stack exchange,提问作者Primer Cielo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 07:36:34