使用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
相关产品推荐
相关产品推荐

