React手风琴组件:实现点击展开当前项、其余项收起的方法
手风琴单开模式实现方案
手风琴组件原代码
const Accordion = (props: AccordionProps) => { const { label, levels, activeId, id } = props const [isExpand, setIsExpand] = useState(false) const onPress = useEvent(() => { setIsExpand(!isExpand) }) return ( <YStack width={'100%'} space={14}> <XStack width="100%" position="relative" justifyContent="flex-start" alignItems="flex-end" onPress={onPress} cursor="pointer" space={10} > <Square animation={'lazy'} rotate={isExpand ? '0deg' : '-90deg'} > <ChevronDownCircle size={'$2'} /> </Square> <Text> {label} </Text> </XStack> <YStack animation={'lazy'} width={'100%'} overflow="hidden" height={!isExpand ? 0 : '100%'} > <Text> {levels} </Text> </YStack> </YStack> ) }
原渲染逻辑
{ScoreLevels.map((level, index) => { return ( <AccordionOne key={`level_${index}`} label={`${level.title}`} levels={level.suggestion} activeId={1} id={level.id} /> ) })}
当前问题
目前每个手风琴项各自维护独立的展开状态,点击时仅当前项切换展开/收起,无法实现“点击某一项展开时,其余项自动收起”的单开效果。
需求
点击选中的手风琴标签时,仅当前标签展开显示内容,其他所有标签自动收起隐藏内容。
修改实现方案
1. 更新Accordion组件逻辑
去掉组件内部的isExpand状态,改为通过父组件传入的activeId与自身id对比判断是否展开;同时新增onToggle回调,通知父组件切换激活项:
// 先更新AccordionProps类型,新增onToggle回调 interface AccordionProps { label: string; levels: string; activeId: number | null; id: number; onToggle: (id: number) => void; } const Accordion = (props: AccordionProps) => { const { label, levels, activeId, id, onToggle } = props; // 通过activeId判断当前项是否展开 const isExpand = activeId === id; const onPress = useEvent(() => { // 点击时通知父组件切换激活的id onToggle(id); }); return ( <YStack width={'100%'} space={14}> <XStack width="100%" position="relative" justifyContent="flex-start" alignItems="flex-end" onPress={onPress} cursor="pointer" space={10} > <Square animation={'lazy'} rotate={isExpand ? '0deg' : '-90deg'} > <ChevronDownCircle size={'$2'} /> </Square> <Text> {label} </Text> </XStack> <YStack animation={'lazy'} width={'100%'} overflow="hidden" height={!isExpand ? 0 : '100%'} > <Text> {levels} </Text> </YStack> </YStack> ); };
2. 修改父组件渲染逻辑
在父组件中维护activeId状态,处理切换逻辑并传递给每个Accordion项:
// 父组件内部 const [activeAccordionId, setActiveAccordionId] = useState<number | null>(null); const handleAccordionToggle = (id: number) => { // 如果点击的是当前激活项,切换为收起;若需始终保持一项展开,直接设置为id即可 setActiveAccordionId(activeAccordionId === id ? null : id); }; // 渲染部分 {ScoreLevels.map((level, index) => { return ( <Accordion key={`level_${index}`} label={level.title} levels={level.suggestion} activeId={activeAccordionId} id={level.id} onToggle={handleAccordionToggle} /> ); })}
逻辑说明
- 父组件统一管理激活项的
activeAccordionId状态,所有手风琴项的展开状态由这个值决定 - 点击手风琴标题时,触发父组件的
handleAccordionToggle函数,更新激活项ID - 每个手风琴项通过对比自身
id和父组件传入的activeId,判断是否需要展开内容
内容的提问来源于stack exchange,提问作者vhison
相关产品推荐
相关产品推荐

