본문 바로가기

FrontEnd

리액트 디자인패턴 : Prop Collections and Getters (프롭 컬렉션 엔 게터 패턴)

반응형

두 줄 요약:

  • Prop Collections and Getters Pattern을 이용해 컴포넌트의 props을 조회, 재사용할 수 있다.
    • 특정 prop을 overriding해서 사용할 수 있다.

토글 버튼을 생각해보자.

  • aria-pressed의 상태도 변경해줘야한다.
  • toggle함수 내에서 on 플래그를 변경해줘야 한다.
  • toggle함수 호출을 위해 onClick 함수도 호출해야 한다.

해당 상태는 캡슐화하여 컴포넌트 안에서만 관리할 수도 있다.

그런데 항상 상태를 캡슐화하는게 답은 아니다.

  • 해당 상태 관리는 컴포넌트 내부로 캡슐화되어도, 해당 상태를 사용자가 조회하여 사용하고 싶을 수 있다.
    • 사용자 props 조회를 위한 props collections
  • 또한, 일부 프롭(onClick)은 디폴트 외에 커스텀 함수를 적용하고 싶어할 수 있다.
    • 사용자가 제공한 props를 컴포넌트에 적용하기 위한 props getters

해당 사용 사례를 지원해보자.

 

토글 상태가 동기화된 컴포넌트를 만들어보자.

위의 UI 요소들을, 훅을 이용해 상태를 공유하는 toggle 버튼으로 만들어 보자.

만들기

1. 보조 함수를 만든다

사용자 onClick을 입력으로 받아 먼저 실행하는 함수를 만든다.

callAll을 통해 사용자의 onClick을 호출하고, 그 다음 toggle을 반드시 호출한다.

사용자는 toggle에 대해 몰라도 된다.

type HandleClick<T> = React.MouseEventHandler<T>;

const callAll: <T>(...handlers: HandleClick<T>[]) => HandleClick<T> = (
  ...handlers
) => (e) => {
  handlers.forEach((f) => f(e));
};

 

2. 훅을 만든다

속성들을 overriding및 추가 정의할 수 있도록 지원한다.

추후 요구사항에 따라 타입을 통해 지원 속성을 확장한다.

(getTogglerProps는 원래 js로 되어있는 예제를 ts로 변경하다 보니, 완벽하게 타입 일치를 적용하기 어려워 보임 ㅠ 그래서 any씀...)

function useToggle() {
  const [on, setOn] = React.useState(false);
  const toggle = () => setOn(!on);
  const outerOn = on;
  function getTogglerProps<T>({
    onClick,
    on,
    ...rest
  }: {
    onClick?: HandleClick<T>;
    on?: boolean;
	[otherProps: string]: any;
  }): { on: boolean; onClick: HandleClick<T>; [key: string]: any } {
    const innerOn = on || outerOn;
    return {
      on: innerOn,
      "aria-pressed": innerOn,
      onClick: onClick ? callAll(onClick, toggle) : toggle,
      ...rest
    };
  }
  return {
    on,
    toggle,
    getTogglerProps
  };
}

getTogglerProps의 사용 방법

  • toggle 컴포넌트의 props을 그대로 다른 컴포넌트에서 활용 가능하다.
  • 일부분을 overriding해서 사용할 수 있다.
function App() {
  const { on, getTogglerProps } = useToggle();
  return (
    <div>
      <Switch {...getTogglerProps<HTMLInputElement>({ on })} />
      <hr />
      <button
        {...getTogglerProps<HTMLButtonElement>({
          "aria-label": "custom-button",
          onClick: () => console.info("onButtonClick"),
          id: "custom-button-id"
        })}
      >
        {on ? "on" : "off"}
      </button>
    </div>
  );
}

 

오픈소스 사용사례

반응형