網站首頁 編程語言 正文
知識點
- emotion語法
- react語法
- css語法
- typescript類型語法
結構分析
根據上圖,我們來分析一下,一個手風琴組件應該包含一個手風琴容器組件和多個手風琴子元素組件。因此,假設我們實現好了所有的邏輯,并寫出使用demo,那么代碼應該如下:
<Accordion defaultIndex="1" onItemClick={console.log}> <AccordionItem label="A" index="1"> Lorem ipsum </AccordionItem> <AccordionItem label="B" index="2"> Dolor sit amet </AccordionItem> </Accordion>
根據以上的結構,我們可以得知,首先容器組件Accordion
會暴露一個defaultIndex屬性以及一個onItemClick事件。顧名思義,defaultIndex代表默認展開的子元素組件AccordionItem
的索引,onItemClick代表點擊每一個子元素組件所觸發的事件。然后,我們可以看到子元素組件有label屬性和index屬性,很顯然,label代表當前子元素的標題,index代表當前子元素組件的索引值,而我們的Lorem ipsum就是子元素的內容。根據這些分析,我們先來實現一下AccordionItem組件。
AccordionItem子組件
首先我們定義好子組件的結構,函數組件寫法如下:
const AccordionItem = (props) => { //返回元素 };
子元素組件分成三個部分,一個容器元素,一個標題元素和一個內容元素,因此我們可以將結構寫成如下:
<div className="according-item-container"> <div className="according-item-header"></div> <div className="according-item-content"></div> </div>
知道了結構之后,我們就知道props會有哪些屬性,首先是索引index屬性,它的類型為string 或者number,然后是判斷內容是否展開的屬性isCollapsed,它的類型是布爾值,其次我們還有渲染標題的屬性label,它應該是一個react節點,類型為ReactNode,同理,還有一個內容屬性即children,類型也應該是ReactNode,最后就是我們要暴露的事件方法handleClick,它的類型應該是一個方法,因此我們可以定義如下的接口:
interface AccordionItemType { index: string | number; label: string; isCollapsed: boolean; //SyntheticEvent代表react合成事件對象的類型 handleClick(e: SyntheticEvent): void; children: ReactNode; }
接口定義好之后,接下來我們就在接口里面拿值(采用對象解構的方式),這些值都算是可選的,即:
const { label, isCollapsed, handleClick, children } = props;
此時我們的AccordionItem子組件應該是如下:
const AccordionItem = (props: Partial<AccordionItemType>) => { const { label, isCollapsed, handleClick, children } = props; return ( <div className={AccordionItemContainer} onClick={handleClick}> <div className={AccordionItemHeader}>{label}</div> <div aria-expanded={isCollapsed} className={`${AccordionItemContent}${ isCollapsed ? ' collapsed' : ' expanded' }`} > {children} </div> </div> ); };
這里我們可以使用emotion/css來寫css類名樣式,代碼如下:
const baseStyle = css` line-height: 1.5715; `; const AccordionItemContainer = css` border-bottom: 1px solid #d9d9d9; `; const AccordionItemHeader = cx( baseStyle, css` position: relative; display: flex; flex-wrap: nowrap; align-items: flex-start; padding: 12px 16px; color: rgba(0, 0, 0, 0.85); cursor: pointer; transition: all 0.3s, visibility 0s; box-sizing: border-box; `, ); const AccordionItemContent = css` color: #000000d9; background-color: #fff; border-top: 1px solid #d9d9d9; transition: all 0.3s ease-in-out; padding: 16px; &.collapsed { display: none; } &.expanded { display: block; } `;
以上的css后面跟模板字符串再跟css樣式就是emotion/css語法,cx
也就是組合樣式寫法,樣式都是常規的寫法,也沒什么好說的。這里有一個難點,那就是display:none和display:block沒有過渡效果,因此可以采用visibility:hidden和opacity:0的方式來替換,但是這里為了簡單,沒考慮動畫效果,所以也就將問題放著,后面有時間再優化。
到目前為止,這個子組件就算是完成了,這也就意味著我們的手風琴組件已經完成一半了,接下來我們來看容器組件Accordion
的寫法。
Accordion容器組件
首先我們先把結構寫好:
const Accordion = (props) => { //后續代碼 };
我們再來分析一下需要傳給Accordion組件的屬性有哪些,很顯然有defaultIndex,onItemClick和children,因此我們可以定義如下的接口:
interface AccordionType { defaultIndex: number | string; onItemClick(key: number | string): void; children: JSX.Element[]; }
注意這里的children不應該是ReactNode,而是JSX.Element元素數組,這是為什么呢,我們后面再來解釋這個問題。現在我們知道了props的屬性之后,我們可以拿到這些屬性,代碼如下:
const Accordion = (props:Partial<AccordionType>) => { const { defaultIndex, onItemClick, children } = props; //后續代碼 };
現在我們再維護一個狀態,用來代表當前顯示的子元素組件的索引,使用useState hook函數,初始化默認值就應該是defaultIndex。如下:
const Accordion = (props:Partial<AccordionType>) => { const { defaultIndex, onItemClick, children } = props; //新增的代碼 const [bindIndex, setBindIndex] = useState(defaultIndex); //后續代碼 };
接下來,我們編寫好容器元素,并寫好樣式,如下所示:
const Accordion = (props: Partial<AccordionType>) => { const { defaultIndex, onItemClick, children } = props; const [bindIndex, setBindIndex] = useState(defaultIndex); return ( <div className={AccordionContainer}></div> ); };
容器元素的樣式如下:
const baseStyle = css` line-height: 1.5715; `; const AccordionContainer = cx( baseStyle, css` box-sizing: border-box; margin: 0; padding: 0; color: #000000d9; font-size: 14px; background-color: #fafafa; border: 1px solid #d9d9d9; border-bottom: 0; border-radius: 2px; `, );
好的,接下來,我們實際上容器元素的子元素應該是多個AccordionItem元素,也正因為如此,這里的children類型就是JSX.Element []
,我們應該如何獲取這些子元素呢?我們應該知道,每一個子元素對應的就是一個節點,在react中用的是鏈表來表示這些節點,每個節點對應的就有個type屬性,我們只需要拿到容器元素的子組件元素中type屬性為AccordionItem的元素數組,如下:
//name不是AccordionItem,代表子元素不是AccordionItem,不是的我們需要過濾掉 const items = children?.filter( (item) => item?.type?.name === 'AccordionItem,代表子元素不是AccordionItem,所以我們需要過濾掉', );
到了這里,我們就知道了,容器元素的子元素是一個數組,我們就需要遍歷,使用map方法,如下:
items?.map(({ props: { index, label, children } }) => ( <AccordionItem key={index} label={label} children={children} isCollapsed={bindIndex !== index} handleClick={() => changeItem(index)} /> ))
請注意這一段代碼:
handleClick={() => changeItem(index)}
這就是我們之前子組件綁定的事件,也是我們需要暴露出去的事件,在這個事件方法中,我們無非執行的就是更改當前被展開元素的索引。所以代碼就很好寫了:
const changeItem = (index: number | string) => { //暴露點擊事件方法接口 if (typeof onItemClick === 'function') { onItemClick(index); } //設置索引 if (index !== bindIndex) { setBindIndex(index); } };
到了這里,我們的一個手風琴組件就完成了,完整代碼如下:
import { cx, css } from '@emotion/css'; import React, { useState } from 'react'; import type { ReactNode, SyntheticEvent } from 'react'; const baseStyle = css` line-height: 1.5715; `; const AccordionContainer = cx( baseStyle, css` box-sizing: border-box; margin: 0; padding: 0; color: #000000d9; font-size: 14px; background-color: #fafafa; border: 1px solid #d9d9d9; border-bottom: 0; border-radius: 2px; `, ); const AccordionItemContainer = css` border-bottom: 1px solid #d9d9d9; `; const AccordionItemHeader = cx( baseStyle, css` position: relative; display: flex; flex-wrap: nowrap; align-items: flex-start; padding: 12px 16px; color: rgba(0, 0, 0, 0.85); cursor: pointer; transition: all 0.3s, visibility 0s; box-sizing: border-box; `, ); const AccordionItemContent = css` color: #000000d9; background-color: #fff; border-top: 1px solid #d9d9d9; transition: all 0.3s ease-in-out; padding: 16px; &.collapsed { display: none; } &.expanded { display: block; } `; interface AccordionItemType { index: string | number; label: string; isCollapsed: boolean; handleClick(e: SyntheticEvent): void; children: ReactNode; } interface AccordionType { defaultIndex: number | string; onItemClick(key: number | string): void; children: JSX.Element[]; } const AccordionItem = (props: Partial<AccordionItemType>) => { const { label, isCollapsed, handleClick, children } = props; return ( <div className={AccordionItemContainer} onClick={handleClick}> <div className={AccordionItemHeader}>{label}</div> <div aria-expanded={isCollapsed} className={`${AccordionItemContent}${ isCollapsed ? ' collapsed' : ' expanded' }`} > {children} </div> </div> ); }; const Accordion = (props: Partial<AccordionType>) => { const { defaultIndex, onItemClick, children } = props; const [bindIndex, setBindIndex] = useState(defaultIndex); const changeItem = (index: number | string) => { if (typeof onItemClick === 'function') { onItemClick(index); } if (index !== bindIndex) { setBindIndex(index); } }; const items = children?.filter( (item) => item?.type?.name === 'AccordionItem', ); return ( <div className={AccordionContainer}> {items?.map(({ props: { index, label, children } }) => ( <AccordionItem key={index} label={label} children={children} isCollapsed={bindIndex !== index} handleClick={() => changeItem(index)} /> ))} </div> ); };
讓我們來看一下效果:
到此為止了,更多React組件的實現,可以訪問react-code-segment。
源碼地址?https://github.com/eveningwater/code-segment-react
原文鏈接:https://segmentfault.com/a/1190000042181290
相關推薦
- 2022-11-27 通過源碼分析Golang?cron的實現原理_Golang
- 2022-06-26 Go語言開源庫實現Onvif協議客戶端設備搜索_Golang
- 2022-12-21 PyGame實現初始化導入所有模塊方法詳解_python
- 2022-02-28 npm install安裝報錯 gyp info it worked if it ends with
- 2022-03-07 C語言switch語句詳解_C 語言
- 2022-12-29 python解決循環依賴的問題分析_python
- 2022-06-20 React函數組件與類組件使用及優劣對比_React
- 2022-11-05 go?module化?import?調用本地模塊?tidy的方法_Golang
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支