当前位置: 首页 > Web前端 > HTML

用React手写一个手风琴组件

时间:2023-03-28 01:01:04 HTML

知识点情感语法react语法css语法typescript类型语法效果来看看我们实现的效果图:结构分析根据上图,我们来分析一下,一个手风琴组件应该包含一个accordion容器组件和多个accordion子组件。因此,假设我们已经实现了所有的逻辑并编写了demo,代码应该如下所示:LoremipsumDolorsitamet根据上面的结构我们可以知道,首先容器组件Accordion会暴露一个defaultIndex属性,一个onItemClick事件。顾名思义,defaultIndex表示默认展开的子元素组件AccordionItem的索引,onItemClick表示点击各个子元素组件触发的事件。然后,我们可以看到子元素组件有一个label属性和一个index属性。显然,label代表当前子元素的标题,index代表当前子元素组件的索引值,我们的Loremipsum就是子元素的内容。根据这些分析,我们先实现AccordionItem组件。AccordionItem子组件首先,我们定义子组件的结构。函数组件写法如下:constAccordionItem=(props)=>{//returnelement};子元素组件分为三个部分,一个容器元素,一个标题元素和一个内容元素,所以我们可以这样写结构:

知道结构后,我们就知道props会有哪些属性。首先是index属性,类型为string或number,然后是属性isCollapsed,判断内容是否展开,类型为布尔值。其次,我们还有渲染标题的属性标签。它应该是一个类型为ReactNode的React节点。同样,还有一个content属性是children,type也应该是ReactNode。最后,我们要暴露的事件方法是handleClick,它的类型应该是方法,所以我们可以定义如下接口:interfaceAccordionItemType{index:string|数字;标签:字符串;已折叠:布尔值;//SyntheticEvent表示react合成事件对象的类型handleClick(e:SyntheticEvent):void;children:ReactNode;}接口定义好后,我们再获取接口中的值(使用对象解构),这些值是可选的,即:const{label,isCollapsed,handleClick,children}=props;此时我们的AccordionItem子组件应该如下:constAccordionItem=(props:Partial)=>{const{label,isCollapsed,handleClick,children}=props;返回(?'collapsed':'expanded'}`}>{children}
);};这里我们可以使用emotion/css来写css类命名样式,代码如下:constbaseStyle=css`line-height:1.5715;`;constAccordionItemContainer=css`border-bottom:1pxsolid#d9d9d9;`;constAccordionItemHeader=cx(baseStyle,css`position:relative;display:flex;flex-wrap:nowrap;align-items:flex-start;padding:12px16px;color:rgba(0,0,0,0.85);cursor:pointer;transition:all0.3s,visibility0s;box-sizing:border-box;`,);constAccordionItemContent=CSS颜色:#000000d9;背景色:#fff;博rder-top:1pxsolid#d9d9d9;过渡:所有0.3秒缓入缓出;填充:16px;&.collapsed{显示:无;}&.expanded{显示:块;}`;上面的css后面跟着一个template字符串后面跟着css样式就是emotion/css语法,cx就是组合样式。款式都是常规款,没什么好说的。这里有一个难点,就是display:none和display:block没有过渡效果,所以可以用Visibility:hidden和opacity:0来代替,但是这里为了简单起见,没有考虑动画效果,所以这个问题先不管了,后面有时间再优化。至此,这个子组件就完成了,也就是说我们的手风琴组件已经完成了一半。接下来我们看一下容器组件Accordion的写法。首先我们来写一下Accordion容器组件的结构:constAccordion=(props)=>{//后续代码};让我们分析一下需要传递给Accordion组件的属性。显然有defaultIndex、onItemClick和children,所以我们可以定义如下接口:interfaceAccordionType{defaultIndex:number|细绳;onItemClick(key:number|string):void;children:JSX.Element[];}注意这里的children应该不是ReactNode,而是JSX.Element元素数组,这是为什么呢,这个问题我们后面再解释。既然我们知道了道具的属性,我们就可以获取它们了。代码如下:constAccordion=(props:Partial)=>{const{defaultIndex,onItemClick,children}=props;//以下代码};现在我们维护另外一个状态,用来表示当前显示的子元素组件的索引。使用useState钩子函数,初始默认值应该是defaultIndex。如下:constAccordion=(props:Partial)=>{const{defaultIndex,onItemClick,children}=props;//添加代码const[bindIndex,setBindIndex]=useState(defaultIndex);//以下代码};接下来我们写容器元素,写样式,如下图:constAccordion=(props:Partial)=>{const{defaultIndex,onItemClick,children}=props;const[bindIndex,setBindIndex]=useState(defaultIndex);返回();};容器元素的样式如下:constbaseStyle=css`line-height:1.5715;`;constAccordionContainer=cx(baseStyle,css`box-sizing:border-box;margin:0;padding:0;color:#000000d9;font-size:14px;background-color:#fafafa;边框:1pxsolid#d9d9d9;-半径:2px;`,);好了,接下来,我们其实应该有多个AccordionItem元素作为容器元素的子元素,正因如此,这里的children类型是JSX.Element[],我们应该如何获取这些子元素呢?我们应该知道,每个子元素对应一个节点。在React中,使用一个链表来表示这些节点。每个节点对应一个类型属性。我们只需要获取容器元素的子组件元素中的type属性是AccordionItem的元素数组,如下://nameisnotanAccordionItem,表示子元素不是AccordionItem。如果不是,我们需要过滤掉constitems=children?.filter((item)=>item?.type?.name==='AccordionItem,也就是说子元素不是AccordionItem,所以我们需要过滤掉',);至此,我们知道容器元素的子元素是一个数组,需要遍历并使用map方法,如下:items?。map(({props:{index,label,children}})=>(changeItem(index)}/>))请注意这段代码:handleClick={()=>changeItem(index)}这是我们之前给子组件绑定的事件,也是我们需要的事件揭露。在这个事件方法中,我们所做的只是改变当前展开的元素的索引所以代码很容易写:constchangeItem=(index:number|string)=>{//暴露点击事件方法接口if(typeofonItemClick==='function'){onItemClick(index);}//设置索引if(index!==bindIndex){setBindIndex(index);}};到这里,我们的一个accordion组件就完成了,完整代码如下:import{cx,css}from'@emotion/css';从“反应”中导入反应,{useState};从'react'导入类型{ReactNode,SyntheticEvent};constbaseStyle=css`line-height:1.5715;`;constAccordionContainer=cx(baseStyle,css`box-sizing:border-box;margin:0;padding:0;color:#000000d9;font-size:14px;background-color:#fafafa;边框:1pxsolid#d9d9d9;border-bottom:0;border-radius:2px;`,);constAccordionItemContainer=css`border-bottom:1pxsolid#d9d9d9;`;constAccordionItemHeader=cx(baseStyle,css`position:relative;display:flex;flex-wrap:nowrap;align-items:flex-start;padding:12px16px;color:rgba(0,0,0,0.85);游标:观点之三;过渡:全部0.3s,可见性0s;框大小:边框框;`,);constAccordionItemContent=css`颜色:#000000d9;背景色:#fff;border-top:1pxsolid#d9d9d9;过渡:所有0.3秒缓入缓出;填充:16px;&.collapsed{显示:无;}&.expanded{显示:块;}`;interfaceAccordionItemType{索引:字符串|数字;标签:字符串;已折叠:布尔值;handleClick(e:SyntheticEvent):void;children:ReactNode;}interfaceAccordionType{defaultIndex:number|细绳;onItemClick(key:number|string):void;children:JSX.Element[];}constAccordionItem=(props:Partial)=>{const{label,isCollapsed,handleClick,children}=道具;返回({label}{children});};constAccordion=(props:Partial)=>{const{defaultIndex,onItemClick,children}=道具;const[bindIndex,setBindIndex]=useState(defaultIndex);constchangeItem=(index:number|string)=>{if(typeofonItemClick==='function'){onItemClick(index);}if(index!==bindIndex){setBindIndex(index);}};constitems=children?.filter((item)=>item?.type?.name==='AccordionItem',);return({items?.map(({props:{index,label,children}})=>(changeItem(index)}/>))});};我们来看看效果:就这样吧,更多React组件的实现,可以访问react-code-segment的源码地址,查看源码地址在这里。我喜欢它并且认为它可以帮助你。我希望你能给我一个赞。你的点赞是我更新文章的最大动力。

最新推荐
猜你喜欢