Typescript 및 TailwindCSS를 사용하여 React에서 Accordion 구성 요소 만들기

저는 현재 React 앱에서 외부 UI 라이브러리를 사용하지 않습니다. 그래서 디자인이 아코디언 컴포넌트를 요구했을 때 처음부터 빌드하는 것이 얼마나 쉬운지 알아내기로 결정했습니다. 결과는 나쁘지 않습니다. 😄

빌딩 블록



아코디언을 만드는 데 필요한 기본 구성 요소는 다음과 같습니다.
  • 일종의 갈매기 모양 아이콘(저는 SVG를 사용했습니다)
  • 상태 변수:
  • 아코디언이 활성(열림) 또는 비활성(닫힘)인지 여부.
  • 활성 상태에 따라 전체 아코디언의 높이가 되어야 합니다.
  • 아코디언이 열린 상태에서 닫힌 상태로(또는 그 반대로) 전환될 때 갈매기 모양 아이콘의 회전 각도입니다.


  • 내 아코디언 구성 요소에 전달하고 싶은 두 가지 소품은 title(아코디언이 닫힐 때 표시되는 텍스트) 및 content(아코디언이 열려 있을 때 표시되는 추가 텍스트)입니다.
    useState React 후크에 익숙하지 않은 경우 괄호 안의 값은 상태 변수의 초기 값입니다. const active 의 시작 값은 false (닫힘)입니다. transform duration-700 easeTailwindCSS 유틸리티 클래스를 나타냅니다(이 클래스는 기본적으로 장면을 설정하여 구성 요소에 어느 시점에서 무언가를 애니메이션으로 만들고 싶다고 알려줍니다).

    import React, { useState } from 'react'
    
    interface AccordionProps {
      title: React.ReactNode
      content: React.ReactNode
    }
    
    export const Accordion: React.FC<AccordionProps> = ({ title, content }) => {
      const [active, setActive] = useState(false)
      const [height, setHeight] = useState('0px')
      const [rotate, setRotate] = useState('transform duration-700 ease')
    
      // ...
    }
    


    레이어 1



    다음 레이어에는 활성 상태를 true 또는 false로 설정하는 일종의 토글 기능이 있습니다. 이 함수는 활성 상태에 따라 높이와 회전도 설정해야 합니다.
    active 상태가 true 일 때 높이를 아직 결정하지 못했습니다. 그것은 아래의 다음 레이어에 있습니다.

    import React, { useState } from 'react'
    
    interface AccordionProps {
      title: React.ReactNode
      content: React.ReactNode
    }
    
    export const Accordion: React.FC<AccordionProps> = ({ title, content }) => {
      const [active, setActive] = useState(false)
      const [height, setHeight] = useState('0px')
      const [rotate, setRotate] = useState('transform duration-700 ease')
    
        function toggleAccordion() {
        setActive(active === false ? true : false)
        // @ts-ignore
        setHeight(active ? '0px' : `${someHeightYetToBeDetermined}px`)
        setRotate(active ? 'transform duration-700 ease' : 'transform duration-700 ease rotate-180')
      }
    
      // ...
    }
    


    레이어 2



    다른 계층으로 올라가면 아코디언의 내부 콘텐츠가 상주할 DOM을 대상으로 하는 방법이 필요합니다. 이를 수행하는 쉬운 방법은 React가 제공하는 유용한 useRef 후크를 사용하는 것입니다. 이를 통해 my <div>가 앉을 content를 구체적으로 대상으로 지정할 수 있습니다.

    이 작업을 수행하기 위해 인라인 CSS를 사용하여 위의 레이어 1에서 소개한 maxHeight 상태 변수와 동일한 height 속성을 설정했습니다. 즉, 활성화되지 않은 경우 높이는 0(숨김)입니다. 이제 contentSpace 를 참조하여 ${contentSpace.current.scrollHeight}px 를 사용하여 아코디언이 활성화될 때의 높이를 결정할 수 있습니다.

    또한 멋진 열기 및 닫기 애니메이션 효과를 원했기 때문에 TailwindCSS를 사용하여 ease-in-out 효과를 설정했습니다.

    import React, { useRef, useState } from 'react'
    import { appConfig } from '../../../../appConfig'
    
    interface AccordionProps {
      title: React.ReactNode
      content: React.ReactNode
    }
    
    export const Accordion: React.FC<AccordionProps> = ({ title, content }) => {
        // ...
    
      const contentSpace = useRef<HTMLDivElement>(null)
    
      function toggleAccordion() {
        setActive(active === false ? true : false)
        // @ts-ignore
        setHeight(active ? '0px' : `${contentSpace.current.scrollHeight}px`)
        setRotate(active ? 'transform duration-700 ease' : 'transform duration-700 ease rotate-180')
      }
    
      return (
            <div
            ref={contentSpace}
            style={{ maxHeight: `${height}` }}
            className="overflow-auto transition-max-height duration-700 ease-in-out"
          >
          <div className="pb-10">{content}</div>
        </div>
        )
    }
    


    함께 모아서



    이제 남은 것은 모든 빌딩 블록을 함께 모으는 것뿐입니다. 전체 Accordion 구성 요소의 모습은 다음과 같습니다.

    여기서 주목해야 할 주요 사항은 다음과 같습니다.
  • 버튼을 만들었습니다. 그 안에 title 소품이 갈매기 모양 아이콘과 함께 있습니다.
  • 레벨 1에서 생성한 onClick 함수에 연결한 이 버튼에 toggleAccordion 핸들러를 추가했습니다.
  • 내 갈매기 모양 아이콘의 rotateclassNames 상태 변수를 추가했습니다. 아코디언의 active 상태에 따라 아이콘을 회전시키는 Tailwind 클래스입니다.

  • import React, { useRef, useState } from 'react'
    import { appConfig } from '../../../../appConfig'
    
    interface AccordionProps {
      title: React.ReactNode
      content: React.ReactNode
    }
    
    export const Accordion: React.FC<AccordionProps> = ({ title, content }) => {
      const [active, setActive] = useState(false)
      const [height, setHeight] = useState('0px')
      const [rotate, setRotate] = useState('transform duration-700 ease')
    
      const contentSpace = useRef(null)
    
      function toggleAccordion() {
        setActive(active === false ? true : false)
        // @ts-ignore
        setHeight(active ? '0px' : `${contentSpace.current.scrollHeight}px`)
        setRotate(active ? 'transform duration-700 ease' : 'transform duration-700 ease rotate-180')
      }
    
      return (
        <div className="flex flex-col">
          <button
            className="py-6 box-border appearance-none cursor-pointer focus:outline-none flex items-center justify-between"
            onClick={toggleAccordion}
          >
            <p className="inline-block text-footnote light">{title}</p>
            <img
              src={`${appConfig.publicUrl}/img/icons/chevron-up.svg`}
              alt="Chevron icon"
              className={`${rotate} inline-block`}
            />
          </button>
          <div
            ref={contentSpace}
            style={{ maxHeight: `${height}` }}
            className="overflow-auto transition-max-height duration-700 ease-in-out"
          >
            <div className="pb-10">{content}</div>
          </div>
        </div>
      )
    }
    


    그리고 그게 다야! 무슨 생각을 했어? 이 문제를 개선할 수 있는 방법이 있습니까? 트위터나 인스타그램에서 채팅합시다.

    좋은 웹페이지 즐겨찾기