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

使用Collapsible实现React Native手风琴及代码优化咨询

优化方案

核心逻辑简化

你的需求是同一时间最多展开1个手风琴,不需要维护全量布尔数组状态,只需要存储当前展开的手风琴索引即可:

  • 初始值设为null,代表所有手风琴全部折叠
  • 点击第id个手风琴时:如果当前展开的就是id,就设为null(收起当前项),否则直接设为id(自动收起其他所有项)

具体代码修改

1. 父组件(InformationScreen.js)调整

import React, { useCallback, useLayoutEffect, useState } from 'react'
import { Text, View, StyleSheet, TouchableWithoutFeedback } from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons'
import BodyText from '../components/Atomic/BodyText';
import Accordion from '../components/Accordion/Accordion';


const InformationScreen = props => {
    // 只存当前展开的索引,null代表全部收起
    const [activeAccordionId, setActiveAccordionId] = useState<number | null>(null)

    // 折叠逻辑简化,用useCallback缓存仅一次
    const handlePress = useCallback((id: number) => {
        setActiveAccordionId(prev => prev === id ? null : id)
    }, [])

    useLayoutEffect(() => {
        props.navigation.setOptions({
            title: 'Information',
            headerStyle: {
                backgroundColor: '#000'
            },
            headerTitleStyle: {
                fontSize: 22,
                color: '#fff'
            },
            headerLeft: () => (
                <View></View>
            ),
            headerRight: () => (
                <TouchableWithoutFeedback onPress={() => {
                    props.navigation.popToTop()
                }}>
                    <View style={styles.iconContainer}>
                        <Icon name="home" size={30} color={'#fff'} />
                    </View>
                </TouchableWithoutFeedback>
            )
        })
    }, [props.navigation]) // 记得加依赖项避免重复执行
    return (
        <View style={styles.container}>
            <Accordion 
                onPress={() => handlePress(0)} 
                title="Lorem" 
                isCollapsed={activeAccordionId !== 0} 
                accordionRender={
                    <View style={styles.accordionItemContainer}>
                        <View style={styles.accordionItemSpacing}>
                            <BodyText>Lorem Ipsum</BodyText>
                            <BodyText>Lorem Ipsum</BodyText>
                        </View>
                        <View>
                            <BodyText>Lorem ipsum</BodyText>
                            <BodyText>Lorem ipsum</BodyText>
                        </View>
                    </View>
                } 
            />
            <Accordion 
                onPress={() => handlePress(1)} 
                title="Bla bla" 
                isCollapsed={activeAccordionId !== 1} 
                accordionRender={
                    <View>
                        <Text>Test</Text>
                        <Text>Test</Text>
                    </View>
                } 
            />
            <Accordion 
                onPress={() => handlePress(2)} 
                title="Bla bla" 
                isCollapsed={activeAccordionId !== 2} 
                accordionRender={
                    <View>
                        <Text>Test</Text>
                        <Text>Test</Text>
                    </View>
                } 
            />
            <Accordion 
                onPress={() => handlePress(3)} 
                title="Bla bla" 
                isCollapsed={activeAccordionId !== 3} 
                accordionRender={
                    <View>
                        <Text>Test</Text>
                        <Text>Test</Text>
                    </View>
                } 
            />
            <Accordion 
                onPress={() => handlePress(4)} 
                title="Lorem Ipsum" 
                isCollapsed={activeAccordionId !== 4} 
                accordionRender={
                    <View style={styles.accordionItemContainer}>
                         <View style={styles.accordionItemSpacing}>
                            <BodyText>Lorem Ipsum</BodyText>
                            <BodyText>Lorem Ipsum</BodyText>
                        </View>
                        <View>
                            <BodyText>Lorem ipsum</BodyText>
                            <BodyText>Lorem ipsum</BodyText>
                        </View>
                    </View>
                } 
            />
        </View>
    );
}

const styles = StyleSheet.create({
    container: {
        backgroundColor: '#000',
        flex: 1,
        borderTopWidth: 2,
        borderTopColor: '#fff'
    }
    ,
    iconContainer: {
        height: 50,
        width: 50,
        borderLeftWidth: 2,
        borderLeftColor: '#fff',
        overflow: 'hidden',
        justifyContent: 'center',
        alignItems: 'flex-end'
    },
    accordionContainerTitle: {
        color: '#000',
        fontSize: 16,
    },
    accordionItemContainer: {
        paddingVertical: 10,
    },
    accordionItemText: {
        color: '#000',
        fontSize: 16,
        marginHorizontal: 10,
    },
    accordionItemSpacing: {
        marginBottom: 10
    }
})

export default InformationScreen;

2. 可选的进一步封装

如果想彻底把状态逻辑和父组件解耦,可以新增一个AccordionGroup组件,把activeId的状态、点击逻辑全部封装在内,父组件只需要传入手风琴配置列表即可,示例:

// AccordionGroup.js
import React, { useState } from 'react';
import Accordion from './Accordion';

const AccordionGroup = ({ list }) => {
  const [activeId, setActiveId] = useState(null);
  return (
    <>
      {list.map((item, index) => (
        <Accordion
          key={index}
          title={item.title}
          accordionRender={item.content}
          isCollapsed={activeId !== index}
          onPress={() => setActiveId(prev => prev === index ? null : index)}
        />
      ))}
    </>
  )
}
export default AccordionGroup;

父组件使用时仅需要传入配置,完全不用管状态逻辑:

// InformationScreen.js 中直接使用
const accordionList = [
  {
    title: 'Lorem',
    content: <View>/* 对应内容 */</View>
  },
  {
    title: 'Bla bla',
    content: <View>/* 对应内容 */</View>
  },
  // 剩余手风琴项
]
// render 部分直接调用组件即可
<AccordionGroup list={accordionList} />

原有的Accordion子组件无需修改即可沿用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 23:06:05