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

react-native-collapsible动态渲染多层嵌套折叠菜单异常如何解决

多层嵌套折叠菜单Native端渲染异常问题

我需要基于API请求返回的数据动态填充菜单,目前已定义示例数据const items,部分菜单项包含子项,且子项可继续嵌套下级子项,因此需要实现多层级嵌套的菜单结构。

我使用react-native-collapsible的Collapsible组件封装了Accordion()折叠组件,针对无子项的菜单封装了AccordionItem()组件。

function App()中会两次渲染菜单项:第一次是手动拼接Accordion和AccordionItem组件实现,第二次是遍历items[]数组,调用RootMenuItem()递归渲染每个顶级菜单项及其所有子项。

目前手动拼接的菜单运行效果符合预期,但通过RootMenuItem()递归渲染的嵌套折叠菜单在Android和iOS端运行异常,仅在snack.io的Web环境测试时功能正常。
请问我的实现存在什么问题?有没有更优的实现方案?

备注:snack.io上测试时动态渲染的项宽度显示异常,可忽略该问题。


问题原因

1. 递归组件作用域错误

你将RootMenuItem递归组件定义在了App组件的内部。React Native中,函数组件每次重渲染都会重新生成内部定义的函数组件,导致递归组件的实例被反复销毁重建,Native端对组件实例变更的敏感度远高于Web端,因此会出现状态错乱、折叠功能失效的问题,而Web端的渲染容错性更高所以暂时正常。

2. 渲染key不唯一

递归渲染时如果仅用数组下标index作为组件key,不同嵌套层级的index会出现重复,React的Diff算法会错误复用同key的组件实例,导致折叠状态错乱。

优化实现方案

第一步:调整组件定义位置

将递归菜单组件移到App组件外部全局定义,避免每次重渲染生成新的组件类型。

第二步:使用唯一稳定key

给每个菜单项分配全局唯一的key,建议用菜单项ID+层级+下标的组合形式,不要单独使用index作为key。

优化后代码示例

import { useState } from 'react';
import { View } from 'react-native';

// 全局定义递归组件,不要放在App内部
const NestedMenuItem = ({ item, level = 0 }) => {
  const [isCollapsed, setIsCollapsed] = useState(true);
  const hasChildren = Array.isArray(item.children) && item.children.length > 0;

  const toggleCollapse = () => setIsCollapsed(prev => !prev);

  return (
    <View style={{ paddingLeft: level * 16 }}>
      {hasChildren ? (
        <Accordion
          title={item.title}
          collapsed={isCollapsed}
          onPress={toggleCollapse}
        >
          {item.children.map((child, index) => (
            <NestedMenuItem
              // 全局唯一key,避免复用错误
              key={`menu-${level}-${child.id ?? index}`}
              item={child}
              level={level + 1}
            />
          ))}
        </Accordion>
      ) : (
        <AccordionItem title={item.title} />
      )}
    </View>
  );
};

// App组件内直接调用即可
function App() {
  return (
    <View>
      {/* 动态渲染部分 */}
      {items.map((topItem, index) => (
        <NestedMenuItem
          key={`top-menu-${topItem.id ?? index}`}
          item={topItem}
          level={0}
        />
      ))}
    </View>
  );
}

可选优化

如果后续需要实现手风琴模式(打开一个菜单自动关闭其他菜单),可以将所有节点的展开状态提升到上层,用对象存储,key为菜单项唯一ID,value为折叠状态,避免状态分散在各个子组件中难以维护。
如果嵌套层级过深时react-native-collapsible仍然出现兼容性问题,可以改用React Native自带的LayoutAnimation配合高度动画自定义折叠组件,原生端兼容性更好。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 05:36:07