HOC라고 읽고 Higher Order Component라고 쓴다

YEONGHUN KO·2022년 2월 17일
0

REACT JS - BASIC

목록 보기
11/30
post-thumbnail

줄여서 HOC라고 한다. 리액트에서 제공하는 특별한 기능은 아니다. 다만 리액트 컴포넌트의 특성을 이용해서 컴포넌트를 손쉽게 재사용가능하기 위한 패턴이라고 보면 될것이다.

이제 간단한 count 컴포넌트 2개를 만들것이다. 하나는 ClickCount로 버튼을 클릭하면 count가 증가하고 다른 하나의 컴포넌트는 HoverCount로 마우스가 올라가면 count가 증가한다.

이 두개의 컴포넌트는 비슷한(아니 거의 같은) state 형태를 공유한다. 따라서 공유되는 state를 한곳에 모아놓고 그 곳에 다른 컴포넌트만 적용하면 되지 않을까?

이때 HOC가 필요하다. 일단 App부터 만들어보자.

App

import ClickCount from './ClickCount';
import HoverCount from './HoverCount';

export default function App() {
  return (
    <>
      <ClickCount />
      <HoverCount name="ZZAK" /> // 차별을 두기 위해 name prop을 보내보았다.
    </>
  );
}

추후 설명하겠지만 위의 두 컴포넌트는 이미 HOC가 적용된 진화된(?) 애들이다.
자 그럼! 이제 공유될 state가 들어가는 공통 컴포넌트를 만들어보자

withCount

보통 HOC의 컨벤션 이름은 with이 접두사로 붙는다. 그리고 withCount는 합쳐질 Component를 인자로 받고 이 인자를 리턴하는 클래스를 리턴한다. 즉, withCount로 인해 한층 진화된 컴포넌트로 변신하여 돌아오는 것이다. 쉽게 비유를 하면 아래와 같다

const IronMan = withSuit(TonyStark)

엄청나게 직관적이고 좋은 비유이다. 토니스타크가 suit 컴포넌트에 힘입어 IronMan이라는 진화된 모습으로 돌아왔다.

그럼 본격적으로 코드를 짜보자

import React from 'react';

function withCount(WrappedComponent, increamentCount) {
  //클래스로 만들거면, React.Component를 상속받는거 잊지말자!!!
  return class WithCount extends React.Component {
    constructor(props) {
      super(props);
      this.state = { count: 0 };
      this.increamentCount = this.increamentCount.bind(this);
    }

    increamentCount() {
      this.setState(prev => ({ count: prev.count + increamentCount }));
    }

    render() {
      return (
        <WrappedComponent
          {...this.props}
          increamentCount={this.increamentCount}
          count={this.state.count}
        >
          {arg => <span>result {arg}</span>}
        </WrappedComponent>
      );
    }
  };
}

export default withCount;

두번째 인자로 incrementCount를 받았다. 증가정도를 설정하도록 한것이다. 사용방법은 IronMan 비유와 같다. 글고 this.props.children의 자리에 함수가 들어갈 수 도 있으니 참고바람! ==>요기로 들으와

그럼 이제 실제로 두 컴포넌트에 withCount를 입혀보자.

ClickCount

import withCount from './withCount';

function ClickCount(props) {
  const { count, increamentCount } = props;
  return <button onClick={increamentCount}>{count} clicked</button>;
}

export default withCount(ClickCount, 10);

withCount에 ClickCount와 증가정도를 pass하였고 강화된 ClickCount를 export하였다. 그럼 withCount에서 알아서 state를 관리해준다. 즉 클릭할때마다 10만큼 증가하는 것이다.

HoverCount

import withCount from './withCount';

function HoverCount(props) {
  const { count, increamentCount, name, children } = props;
  return (
    <>
      <h1 onMouseOver={increamentCount}>
        {name} Hover {children(count)}
      </h1>
    </>
  );
}

export default withCount(HoverCount, 5);

children을 통해서 withCount에서 보낸 함수를 사용할 수 있다. 그리고 name prop은 App에서 보낸것이다.

그럼 gif처럼 작동한다!!

이 엄청난 예시는 요기로 부터 오셨습니다

profile
'과연 이게 최선일까?' 끊임없이 생각하기

0개의 댓글