为Next.js+TypeScript的Accordion组件添加嵌套功能
实现嵌套式Next.js + TypeScript Accordion组件
以下是针对你的两个技术挑战的解决方案:
1. TypeScript递归类型定义修改
首先需要在接口中定义递归的数据结构,让accordionItems的元素支持嵌套的children字段,同时兼容单个子项或子项数组的场景:
// IAccordion.ts import React from 'react'; // 递归定义Accordion项的数据结构 export interface IAccordionItemData { id: string | number; title: string | React.ReactElement; content: string | React.ReactElement; children?: IAccordionItemData | IAccordionItemData[]; // 支持单个子项或子项数组 } export interface IAccordion { accordionItems: IAccordionItemData[]; isMultiple?: boolean; className?: string; itemClassName?: string; } export interface IAccordionItem extends Pick<IAccordion, 'isMultiple' | 'itemClassName'> { accordionItem: IAccordionItemData; isActive: boolean; toggleOnlyOneAccordionItemHandler: (id: IAccordionItemData['id']) => void; }
2. 递归组件实现修改
在AccordionItem组件中,判断当前项是否存在children,如果有则递归渲染Accordion组件,同时处理单个子项转数组的逻辑,并添加嵌套样式区分层级:
修改后的Accordion组件代码
// Accordion.tsx import React, { useEffect, useState } from 'react'; import classes from './Accordion.module.scss'; import { IAccordion, IAccordionItem, IAccordionItemData } from './IAccordion'; const AccordionItem: React.FC<IAccordionItem> = ({ accordionItem, isMultiple, isActive = false, toggleOnlyOneAccordionItemHandler, itemClassName, }) => { const [isVisible, setIsVisible] = useState(isActive); useEffect(() => { setIsVisible(isActive); }, [isActive]); const toggleAccordionItemHandler = () => { setIsVisible(prevState => !prevState); }; // 统一将children转换为数组,兼容单个子项或数组的情况 const getChildrenItems = (): IAccordionItemData[] => { if (!accordionItem.children) return []; return Array.isArray(accordionItem.children) ? accordionItem.children : [accordionItem.children]; }; return ( <div className={`${classes.item} ${itemClassName}`}> <div className={classes.title} onClick={() => isMultiple ? toggleAccordionItemHandler() : toggleOnlyOneAccordionItemHandler(accordionItem.id) } > <h2>{accordionItem.title}</h2> <span>{isVisible ? '-' : '+'}</span> </div> <div className={`${classes.content} ${isVisible ? classes.show : ''}`} > {accordionItem.content} {/* 递归渲染嵌套Accordion,仅在当前项展开时显示 */} {isVisible && getChildrenItems().length > 0 && ( <Accordion accordionItems={getChildrenItems()} isMultiple={isMultiple} className={classes['nested-accordion']} itemClassName={itemClassName} /> )} </div> </div> ); }; export const Accordion: React.FC<IAccordion> = ({ accordionItems, isMultiple = true, className, itemClassName, }) => { const [selectedAccordionItemId, setSelectedAccordionItemId] = useState<null | number | string>(null); const toggleOnlyOneAccordionItemHandler = (accordionItemId: number | string) => { setSelectedAccordionItemId(prevId => prevId === accordionItemId ? null : accordionItemId); }; return ( <div className={`${classes.accordion} ${className}`}> {accordionItems.map(accordionItem => ( <React.Fragment key={accordionItem.id}> <AccordionItem accordionItem={accordionItem} isMultiple={isMultiple} itemClassName={itemClassName} isActive={selectedAccordionItemId === accordionItem.id} toggleOnlyOneAccordionItemHandler={toggleOnlyOneAccordionItemHandler} /> </React.Fragment> ))} </div> ); };
嵌套样式调整
在样式文件中添加嵌套Accordion的样式,让层级更清晰:
/* Accordion.module.scss */ .accordion{ width: 500px; } /* 嵌套Accordion的缩进和宽度调整 */ .accordion.nested-accordion { width: calc(100% - 20px); margin: 10px 0 0 20px; } .item{ background: #f0ebe1; margin-bottom: 5px; } .title{ color: #85662b; display: flex; justify-content: space-between; align-items: center; cursor: pointer; padding: 10px 20px; } .content{ color: #8b7f75; max-height: 0; overflow: hidden; transition: max-height 0.5s cubic-bezier(0,1,0,1); } .content.show{ height: auto; max-height: 9999px; transition: max-height 0.5s cubic-bezier(1,0,1,0); padding: 10px 20px; }
使用示例
现在可以按照你期望的结构使用组件,支持任意层级的嵌套:
<Accordion accordionItems={[ { title: 'Question1', id: 'Question1', content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cumque dignissimos dolore earum e' + 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cumque dignissimos dolore earum e Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cumque dignissimos dolore earum e' + ' Cumque dignissimos dolore earum explicabo incidunt omnis pariatur repellat sapiente soluta voluptate?', children: { title: 'Question1.1', id: 'Question1.1', content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Cumque dignissimos dolore earum e', children: { title: 'Question1.1.1', id: 'Question1.1.1', content: 'Lorem simos dolore earum e', } } }, { title: 'Question2', id: 'Question2', content: 'Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ab accusantium ad animi aspernatur aut beatae culpa eius esse expedita fugit hic ipsa laboriosam laborum maxime optio, quis sint tempora ullam vitae voluptatum. Eum, facere, possimus!', }, ]} />
内容的提问来源于stack exchange,提问作者Ali Ehyaie
相关产品推荐
相关产品推荐

