React Hook 디자인패턴

JaeEun Lee·2024년 2월 25일
0

React

목록 보기
7/10

이글은 아래 링크를 번역한 글입니다.
원문은 여기(영문)

React에서 Hook은?

  • React에서는 Hooks을 사용함으로서 코드가 자연스러워진다.
  • Hooks는 함수형프로그래밍에서는 클래스를 대체하는 특별한 함수이다.
  • 객체지향보다는 기능지향으로 접근

클래스를 함수로 대체해야 하는 이유는?

  • 더 선형적 → 코드 복잡성 감소
  • 예측할 수 없는 버그방지 → 코드 견고성 향상
  • 테스트, 디버그, 유지관리 용이

사용 예

상위구성요소와 하위구성요소 간의 통신에 후크를 사용해보자. 예로 하위구성요소에 Accordion을 포함하는 상위구성요소 Home이 있다고 하자.
요구사항은,
1. Home에서는 Accordion을에 대한 Label과 하위항목을 설정해야 한다.
2. Accordion은 홈에 상태(펼치기/닫기)를 알려야 한다.

첫 번째 요구사항은 구현하기 쉽다. 아래코드처럼 Home은 Props를 Accordion에 전달할 수 있어야 한다.

// accordion.types.ts
export type AccordionProps = {
  label: string
  children: ReactNode
}

// accordion.tsx
export const Accordion = ({ label, children }: AccordionProps) => {
  return (
    <div>
      <p>{label}</p>
      {children}
    </div>
  )
}

// home.tsx
export const Home = () => {
  return (
    <div>
      <Accordion label="Accordion label">
        <div>Accordion content</div>
      </Accordion>
    </div>
  )
})

두 번째 요구사항을 좀더 구체화해서 Accordion을 아래 기능을 추가해 내부상태로 expended 를 가지고 toggle 내부기능이 있다고 하자.
여기서 Hook를 사용한다. Accordion을 논리를 집중화하기 위해 useAccordion 즉 커스텀후크를 작성한다.

// accordion.hook.tsx
export const useAccordion = () => {
  const [expanded, setExpanded] = useState(false)

  const toggle = () => setExpanded((x) => !x)
  
  return { expanded, toggle }
}

// accordion.tsx
export const Accordion = ({ label, children }: AccordionProps) => {
  const { expanded, toggle } = useAccordion()
  return (
    <div onClick={toggle}> 
      <p>{label}</p>
      {expanded && children}
    </div>
  )
}

이제 아코디언도 작동하게 되었다. 하지만 Accordion은 Home에 내부상태를 어떻게 알릴 수 있을까?
아마도 가장 먼저 떠오르는 아이디어는 Accordion props에 onChange 콜백을 추가하여 Home이 알림을 받는 함수를 전달할 수 있도록 하는 것이다.

이 방법은 나름 괜찮지만 다음과 같은 두 가지 이유로 인해 번거롭다.

  • Home은 변경될 때 Accordion 상태와 명시적으로 정렬되도록 자체 내부상태를 정의해야 한다.
  • 상태가 두 개 이상인 경우 부모는 두개 이상의 콜백이 필요할 수 있다.

여기서 제대로 Hook을 활용하려면 아래와 같이 Hook에서 반환된 모든것을 props로 받아들이는 것이다.

// accordion.types.ts
export type AccordionProps = ReturnType<typeof useAccordion> & {
  label: string
  children: ReactNode
  expanded: boolean;
  toggle: () => void;
}

// accordion.tsx
import React from 'react';
import { AccordionProps } from './accordion.types';

export const Accordion = ({
  label,
  children,
  expanded,
  toggle
}: AccordionProps) => {
  return (
    <div onClick={toggle}>
      <p>{label}</p>
      {expanded && children}
    </div>
  );
};

// home.tsx
import React from 'react';
import { Accordion } from './accordion';
import { useAccordion } from './useAccordion'; // Assuming you have this hook

export const Home = () => {
  const { expanded, toggle } = useAccordion();
  return (
    <div>
      <p>Accordion state: {expanded ? 'Expanded' : 'Collapsed'}</p>
      <Accordion label="Accordion label" expanded={expanded} toggle={toggle}>
        <div>Accordion content</div>
      </Accordion>
    </div>
  );
};

useAccordion()의 결과는 Accordion props로 이동되었다.
그리고 Home은 Accordion내부상태에 접근하거나 또는 내부함수에 invoke/override를 하려면 아래와 같다.

// home.tsx
export const Home = () => {
  const { expanded, toggle } = useAccordion()

  const customToggle = () => {
    // Override here
    toggle() // Invoke here
  }

  return (
    <div>
      <p>Accordion state: {expanded}</p>
      <Accordion
         label="Accordion label"
         expanded={expanded}
         toggle={customToggle}
      >
        <div>Accordion content</div>
      </Accordion>
    </div>
  )
})

Home에서 toggle 기능을 호출하고 재정의할 수 있다는 사실은 정말 강력한 기능이다.

요약하면 후크 디자인 패턴은 다음과 같은 장점이 있다.

  • 모든 형태의 코드중복을 방지한다.
  • 상위구성요소와 하위구성요소는 서로 다른 두가지 상태를 관리하는 복잡함이 필요없고 동일한 상태를 공유할 수 있다.
  • 부모는 내부 함수를 직접 호출하고 재정의하여 자식 내부의 논리를 제어할 수도 있다.
profile
공업철학프로그래머

0개의 댓글