ContextAPI + styled-components로 재사용성 있는 아코디언 컴포넌트 만들기

이정수·2023년 9월 6일
1

ReactDeepDive

목록 보기
3/4
post-thumbnail

ContextAPI를 이용해 재사용성있는 컴포넌트 만들기

contextAPI + styledComponent를 가지고 재사용이 가능한 아코디언 UI를 만들어보자

아코디언 UI란 ?
모바일/웹에서 콘텐츠를 접어서 페이지길이를 관리할수 있도록 만든 UI이다. 시각적인 복잡성을 줄이고 사용자가 당면한 작업과 가장 관련이 있는 콘텐츠에 집중할 수 있도록 한다.

먼저 안좋은 예시인 StpidAccordion과 좋은 예시인 SmartAccordion을 랜더링할 AccordionExample 파일이다.

export default function AccorianExample(){
  return(
    <>
      <h2>ContextAPI를 이용한 재사용성있는 컴포넌트 만들기</h2>
      
      <StupidAccordion />

      <SmartAccordion>
        <SmartAccordionHeader>I'm smart Accordion</SmartAccordionHeader>
        <SmartAccordionBody>BBody</SmartAccordionBody>
      </SmartAccordion>
    </>
  );
}

Stupid Accordion

기본적으로 아코디언 UI는 헤더영역을 클릭하면 BODY 부분이 펼쳐지고 다시 헤더를 클릭하면 BODY가 가려지는 UI이다. 때문에 해당 아코디언을 만들기 위해서는 펼쳐져 있는 지 여부를 확인하는 isOpen 상태값이 하나 필요하다.

export default function StupidAccordion(){
  const [isOpen, setIsOpen] = useState(false);
  
  return(
    <S.Wrapper>
      <S.HeaderWrapper onClick={()=>setIsOpen(!isOpen)}>
        I'm stupid Accordion
      </S.HeaderWrapper>
      <S.BodyWrapper isOpen={isOpen}>Body</S.BodyWrapper>
    </S.Wrapper>
  )
}

헤더 영역의 onClick 이벤트에 setIsOpen()을 걸어서 클릭할때마다 바꿔준다.
바디 영역은 isOpen값에 따라 영역을 숨기거나 드러내야 한다.

여기서 중요한 점은 헤더와 바디영역을 모두 isopen이라는 상태값을 필요로한다는 점이다. 때문에 요소들을 담고 있는 컴포넌트에 상태값을 위치시킨다.

만약 이 UI를 한번만 사용한다면 더 복잡한 코드는 필요하지 않을 것이다.
그러나 기능을 같은데 들어가는 콘텐츠,스타일이 다른 컴포넌트가 필요하거나 혹은 여러개 필요할 경우 똑같은 기능을 가진 컴포넌트를 여러번 만드는 것은 비효율 적일 것이다.

Smart Accordion

const AccordionContext = createContext();
const useAccordionContext = () => useContext(AccordionContext);


export  function SmartAccordion({children}){
  const [isOpen, setIsOpen] = useState(false);

  const value = {
    isOpen,
    setIsOpen
  }

  return(
    <>
      <AccordionContext.Provider value = {value}>
        <S.Wrapper>{children}</S.Wrapper>
      </AccordionContext.Provider>
    </>
  )
};

export  function SmartAccordionHeader (props)  {
  const {isOpen, setIsOpen} = useAccordionContext();
  return (
    <S.HeaderWrapper 
      onClick = {() => setIsOpen(!isOpen)}
      >
      {props.children}
    </S.HeaderWrapper>
  )
};


export  function SmartAccordionBody (props) {
  const {isOpen} = useAccordionContext();

  return(
    <S.BodyWrapper 
      isOpen = {isOpen}
      >
      {props.children}
    </S.BodyWrapper>
  )
}

살짝 길어보이지만 찬찬히 둘러보자.

이 파일은 3개의 함수 컴포넌트로 나누어져 있다.

  1. isOpen 상태값을 가지고 있는 SmartAccordion
  2. 헤더 영역을 담당하는 SmartAccordionHeader
  3. 바디 영역을 담당하는 SmartAccordionBody

포인트는 이렇게 분리되어 있어도 header, body에서 isOpen 상태값에 접근이 가능하도록 해야 한다.

*Provider 내부에 들어있는 children 컴포넌트들만 useContext를 이용해서 context의 값에 접근할 수 있다.
=> 2. 헤더와 3.바디가 1.의 자식으로 들어가도록 할것이다.

<SmartAccordion>
  <SmartAccordionHeader>I'm smart Accordion</SmartAccordionHeader>
  <SmartAccordionBody>BBody</SmartAccordionBody>
</SmartAccordion>

smartAccordion으로 감싸주고 안에 헤더와 바디를 넣어준다.

stupid의 경우 contents를 변경하려고 한다면 컴포넌트 폴더에 직접 들어가서 변경해줘야 한다는 단점이 있다.

styped-components

추가적으로 이 아코디언 컴포넌트에 텍스트컨텐츠와 스타일을 변경하고 싶다면 어떻게 해야 할까?

이럴경우 styled-componet를 사용하면 html element 뿐 아니라 React component도 스타일링 할 수 있다.
이번에는 파란색이 아닌 초록색을 지닌 아코디언UI를 만들어보자

  1. SmartAccordion Header/Body 에 새로운 스타일링을 입힌 컴포넌트를 만들어 준다.
const StyledSmartAccordionHeader = styled(SmartAccordionHeader)`
  background-color: green;
`;

const StyledSmartAccordionBody = styled(SmartAccordionBody)`
  border: 1px solid green;
`
  1. 있던 자리에 컴포넌트 넣어주기
      <SmartAccordion>
        <StyledSmartAccordionHeader>I'm smart Accordion but diff color</StyledSmartAccordionHeader>
        <StyledSmartAccordionBody>BBody</StyledSmartAccordionBody>
      </SmartAccordion>
  1. 컴포넌트 자체에서 props.className을 가장 바깥태그에 className 값으로 넣어준다.
export  function SmartAccordionHeader (props)  {
  const {isOpen, setIsOpen} = useAccordionContext();
  return (
    <S.HeaderWrapper 
      onClick = {() => setIsOpen(!isOpen)}
      className = {props.className}
      >
      {props.children}
    </S.HeaderWrapper>
  )
};


export  function SmartAccordionBody (props) {
  const {isOpen} = useAccordionContext();

  return(
    <S.BodyWrapper 
      isOpen = {isOpen}
     className = {props.className}
      >
      {props.children}
    </S.BodyWrapper>
  )
}

이렇게 컨텐츠의 내용과 스타일 모두 변경할 수 있는 재사용가능한 아코디언 컴포넌트를 만들어 보았다!

profile
keep on pushing

0개의 댓글