欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React手寫(xiě)一個(gè)手風(fēng)琴組件示例

 更新時(shí)間:2022年07月21日 17:32:14   作者:夕水  
這篇文章主要為大家介紹了React手寫(xiě)一個(gè)手風(fēng)琴組件示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

知識(shí)點(diǎn)

  • emotion語(yǔ)法
  • react語(yǔ)法
  • css語(yǔ)法
  • typescript類(lèi)型語(yǔ)法

結(jié)構(gòu)分析

根據(jù)上圖,我們來(lái)分析一下,一個(gè)手風(fēng)琴組件應(yīng)該包含一個(gè)手風(fēng)琴容器組件和多個(gè)手風(fēng)琴子元素組件。因此,假設(shè)我們實(shí)現(xiàn)好了所有的邏輯,并寫(xiě)出使用demo,那么代碼應(yīng)該如下:

<Accordion defaultIndex="1" onItemClick={console.log}>
   <AccordionItem label="A" index="1">
     Lorem ipsum
   </AccordionItem>
   <AccordionItem label="B" index="2">
      Dolor sit amet
   </AccordionItem>
</Accordion>

根據(jù)以上的結(jié)構(gòu),我們可以得知,首先容器組件Accordion會(huì)暴露一個(gè)defaultIndex屬性以及一個(gè)onItemClick事件。顧名思義,defaultIndex代表默認(rèn)展開(kāi)的子元素組件AccordionItem的索引,onItemClick代表點(diǎn)擊每一個(gè)子元素組件所觸發(fā)的事件。然后,我們可以看到子元素組件有l(wèi)abel屬性和index屬性,很顯然,label代表當(dāng)前子元素的標(biāo)題,index代表當(dāng)前子元素組件的索引值,而我們的Lorem ipsum就是子元素的內(nèi)容。根據(jù)這些分析,我們先來(lái)實(shí)現(xiàn)一下AccordionItem組件。

AccordionItem子組件

首先我們定義好子組件的結(jié)構(gòu),函數(shù)組件寫(xiě)法如下:

const AccordionItem = (props) =&gt; {
   //返回元素
};

子元素組件分成三個(gè)部分,一個(gè)容器元素,一個(gè)標(biāo)題元素和一個(gè)內(nèi)容元素,因此我們可以將結(jié)構(gòu)寫(xiě)成如下:

<div className="according-item-container">
   <div className="according-item-header"></div>
   <div className="according-item-content"></div>
</div>

知道了結(jié)構(gòu)之后,我們就知道props會(huì)有哪些屬性,首先是索引index屬性,它的類(lèi)型為string 或者number,然后是判斷內(nèi)容是否展開(kāi)的屬性isCollapsed,它的類(lèi)型是布爾值,其次我們還有渲染標(biāo)題的屬性label,它應(yīng)該是一個(gè)react節(jié)點(diǎn),類(lèi)型為ReactNode,同理,還有一個(gè)內(nèi)容屬性即children,類(lèi)型也應(yīng)該是ReactNode,最后就是我們要暴露的事件方法handleClick,它的類(lèi)型應(yīng)該是一個(gè)方法,因此我們可以定義如下的接口:

interface AccordionItemType {
  index: string | number;
  label: string;
  isCollapsed: boolean;
  //SyntheticEvent代表react合成事件對(duì)象的類(lèi)型
  handleClick(e: SyntheticEvent): void;
  children: ReactNode;
}

接口定義好之后,接下來(lái)我們就在接口里面拿值(采用對(duì)象解構(gòu)的方式),這些值都算是可選的,即:

const { label, isCollapsed, handleClick, children } = props;

此時(shí)我們的AccordionItem子組件應(yīng)該是如下:

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來(lái)寫(xiě)css類(lèi)名樣式,代碼如下:

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語(yǔ)法,cx也就是組合樣式寫(xiě)法,樣式都是常規(guī)的寫(xiě)法,也沒(méi)什么好說(shuō)的。這里有一個(gè)難點(diǎn),那就是display:none和display:block沒(méi)有過(guò)渡效果,因此可以采用visibility:hidden和opacity:0的方式來(lái)替換,但是這里為了簡(jiǎn)單,沒(méi)考慮動(dòng)畫(huà)效果,所以也就將問(wèn)題放著,后面有時(shí)間再優(yōu)化。

到目前為止,這個(gè)子組件就算是完成了,這也就意味著我們的手風(fēng)琴組件已經(jīng)完成一半了,接下來(lái)我們來(lái)看容器組件Accordion的寫(xiě)法。

Accordion容器組件

首先我們先把結(jié)構(gòu)寫(xiě)好:

const Accordion = (props) =&gt; {
  //后續(xù)代碼
};

我們?cè)賮?lái)分析一下需要傳給Accordion組件的屬性有哪些,很顯然有defaultIndex,onItemClick和children,因此我們可以定義如下的接口:

interface AccordionType {
  defaultIndex: number | string;
  onItemClick(key: number | string): void;
  children: JSX.Element[];
}

注意這里的children不應(yīng)該是ReactNode,而是JSX.Element元素?cái)?shù)組,這是為什么呢,我們后面再來(lái)解釋這個(gè)問(wèn)題。現(xiàn)在我們知道了props的屬性之后,我們可以拿到這些屬性,代碼如下:

const Accordion = (props:Partial<AccordionType>) => {
  const { defaultIndex, onItemClick, children } = props;
  //后續(xù)代碼
};

現(xiàn)在我們?cè)倬S護(hù)一個(gè)狀態(tài),用來(lái)代表當(dāng)前顯示的子元素組件的索引,使用useState hook函數(shù),初始化默認(rèn)值就應(yīng)該是defaultIndex。如下:

const Accordion = (props:Partial&lt;AccordionType&gt;) =&gt; {
  const { defaultIndex, onItemClick, children } = props;
  //新增的代碼
  const [bindIndex, setBindIndex] = useState(defaultIndex);
  //后續(xù)代碼
};

接下來(lái),我們編寫(xiě)好容器元素,并寫(xiě)好樣式,如下所示:

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;
  `,
);

好的,接下來(lái),我們實(shí)際上容器元素的子元素應(yīng)該是多個(gè)AccordionItem元素,也正因?yàn)槿绱?,這里的children類(lèi)型就是JSX.Element [],我們應(yīng)該如何獲取這些子元素呢?我們應(yīng)該知道,每一個(gè)子元素對(duì)應(yīng)的就是一個(gè)節(jié)點(diǎn),在react中用的是鏈表來(lái)表示這些節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)對(duì)應(yīng)的就有個(gè)type屬性,我們只需要拿到容器元素的子組件元素中type屬性為AccordionItem的元素?cái)?shù)組,如下:

//name不是AccordionItem,代表子元素不是AccordionItem,不是的我們需要過(guò)濾掉
const items = children?.filter(
    (item) => item?.type?.name === 'AccordionItem,代表子元素不是AccordionItem,所以我們需要過(guò)濾掉',
 );

到了這里,我們就知道了,容器元素的子元素是一個(gè)數(shù)組,我們就需要遍歷,使用map方法,如下:

items?.map(({ props: { index, label, children } }) => (
  <AccordionItem
     key={index}
     label={label}
     children={children}
     isCollapsed={bindIndex !== index}
     handleClick={() => changeItem(index)}
  />
))

請(qǐng)注意這一段代碼:

handleClick={() => changeItem(index)}

這就是我們之前子組件綁定的事件,也是我們需要暴露出去的事件,在這個(gè)事件方法中,我們無(wú)非執(zhí)行的就是更改當(dāng)前被展開(kāi)元素的索引。所以代碼就很好寫(xiě)了:

const changeItem = (index: number | string) => {
   //暴露點(diǎn)擊事件方法接口
   if (typeof onItemClick === 'function') {
     onItemClick(index);
   }
   //設(shè)置索引
   if (index !== bindIndex) {
     setBindIndex(index);
   }
};

到了這里,我們的一個(gè)手風(fēng)琴組件就完成了,完整代碼如下:

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>
  );
};

讓我們來(lái)看一下效果:

到此為止了,更多React組件的實(shí)現(xiàn),可以訪問(wèn)react-code-segment

源碼地址 https://github.com/eveningwater/code-segment-react

以上就是React手寫(xiě)一個(gè)手風(fēng)琴組件示例的詳細(xì)內(nèi)容,更多關(guān)于React手風(fēng)琴組件的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React報(bào)錯(cuò)解決之ref返回undefined或null

    React報(bào)錯(cuò)解決之ref返回undefined或null

    最近使用react做個(gè)滾動(dòng)監(jiān)聽(tīng)獲取更多數(shù)據(jù)效果,當(dāng)想獲取dom時(shí)發(fā)現(xiàn)怎么也獲取不到,下面這篇文章主要給大家介紹了關(guān)于React報(bào)錯(cuò)解決之ref返回undefined或null的相關(guān)資料,需要的朋友可以參考下
    2022-08-08
  • React-Native中禁用Navigator手勢(shì)返回的示例代碼

    React-Native中禁用Navigator手勢(shì)返回的示例代碼

    本篇文章主要介紹了React-Native中禁用Navigator手勢(shì)返回的示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下
    2017-09-09
  • React Hook 父子組件相互調(diào)用函數(shù)方式

    React Hook 父子組件相互調(diào)用函數(shù)方式

    這篇文章主要介紹了React Hook 父子組件相互調(diào)用函數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-09-09
  • React報(bào)錯(cuò)之Object?is?possibly?null的問(wèn)題及解決方法

    React報(bào)錯(cuò)之Object?is?possibly?null的問(wèn)題及解決方法

    這篇文章主要介紹了React報(bào)錯(cuò)之Object?is?possibly?null的問(wèn)題,造成 "Object is possibly null"的錯(cuò)誤是因?yàn)閡seRef()鉤子可以傳遞一個(gè)初始值作為參數(shù),而我們傳遞null作為初始值,本文給大家分享詳細(xì)解決方法,需要的朋友可以參考下
    2022-07-07
  • 采用React編寫(xiě)小程序的Remax框架的編譯流程解析(推薦)

    采用React編寫(xiě)小程序的Remax框架的編譯流程解析(推薦)

    這篇文章主要介紹了采用React編寫(xiě)小程序的Remax框架的編譯流程解析(推薦),本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • React中ES5與ES6寫(xiě)法的區(qū)別總結(jié)

    React中ES5與ES6寫(xiě)法的區(qū)別總結(jié)

    這篇文章主要總結(jié)介紹了關(guān)于React中ES5與ES6的寫(xiě)法區(qū)別,文中介紹的非常詳細(xì),相信對(duì)大家具有一定的參考價(jià)值,需要的朋友們下面來(lái)一起看看吧。
    2017-04-04
  • react如何實(shí)現(xiàn)側(cè)邊欄聯(lián)動(dòng)頭部導(dǎo)航欄效果

    react如何實(shí)現(xiàn)側(cè)邊欄聯(lián)動(dòng)頭部導(dǎo)航欄效果

    這篇文章主要介紹了react如何實(shí)現(xiàn)側(cè)邊欄聯(lián)動(dòng)頭部導(dǎo)航欄效果,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • React中的生命周期詳解

    React中的生命周期詳解

    這篇文章主要介紹了React中的生命周期,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧
    2022-09-09
  • 使用 React 和 Threejs 創(chuàng)建一個(gè)VR全景項(xiàng)目的過(guò)程詳解

    使用 React 和 Threejs 創(chuàng)建一個(gè)VR全景項(xiàng)目的過(guò)程詳解

    這篇文章主要介紹了使用 React 和 Threejs 創(chuàng)建一個(gè)VR全景項(xiàng)目的過(guò)程詳解,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-04-04
  • react-router-dom v6版本實(shí)現(xiàn)Tabs路由緩存切換功能

    react-router-dom v6版本實(shí)現(xiàn)Tabs路由緩存切換功能

    今天有人問(wèn)我怎么實(shí)現(xiàn)React-Router-dom類(lèi)似標(biāo)簽頁(yè)緩存,很久以前用的是react-router v5那個(gè)比較容易實(shí)現(xiàn),v6變化挺大,但了解react的機(jī)制和react-router的機(jī)制就容易了,本文介紹react-router-dom v6版本實(shí)現(xiàn)Tabs路由緩存切換,感興趣的朋友一起看看吧
    2023-10-10

最新評(píng)論