High Order Component

jaemin·2020년 12월 23일
1

리액트

목록 보기
10/16
post-thumbnail

High Order Component

HOC란,

  • 리액트에서 컴포넌트 로직을 재사용하기 위해 사용한다.
  • 리액트 API 종류가 아니다.
  • 리액트의 구성적 특성에서 나온 패턴이다.(react's compositional nature)

정리하자면, 컴포넌트 로직을 재사용하고 싶어서 만든 패턴이 바로 고차 컴포넌트이다. 구체적으로 고차 컴포넌트는 컴포넌트를 인자로 받아와 새로운 컴포넌트를 반환하는 함수이다.

컴포넌트는 props를 UI로 변환하는 반면에, 상위 컴포넌트는 컴포넌트를 마치 props처럼 다른 컴포넌트로 변환한다.

스크린샷 2020-12-23 오후 12 42 35

HOC는 리덕스 connect나 Relay같은 리액트 라이브러리에서 일반이다.

react router에서 이미 HOC를 본 적이 있다. 바로 withRouter()이다. 보통 with가 붙은 함수가 HOC인 경우가 많다.

import React from "react";
import { withRouter } from "react-router-dom";

const LoginButton = props => {
  console.log(props);
  function login() {
    setTimeout(() => {
      props.history.push("/");
    }, 1000);
  }
  return <button onClick={login}>로그인하기</button>;
};

export default withRouter(LoginButton);

withRouter라는 고차 컴포넌트가 인수로 LoginButton컴포넌트를 받아 새로운 컴포넌트를 반환해준다.

react's compositional nature 이란?

이해를 돕기 위해 예시를 들어보자.

A와 B라는 친구가 있다고 가정해보자.

A라는 친구가 나에게 빼뺴로를 주었는데, 내가 빼빼로의 초코만 먹고 과자 부분만 모아서 다시 A에게 돌려주었다. 이것은 compositional 하지 않다.

반면, B라는 친구가 준 빼빼로는 받아서 건드리지 않고 몽쉘, 새우깡 같은 것을 추가해서 선물세트로 만들어 B에게 다시 돌려준다. 이것은 compositional 하다고 한다.

빼뺴로 = 인자로 받은 컴포넌트

선물세트 = HOC가 반환하는 새로운 컴포넌트

A의 경우 : 컴포넌트를 상속받아 그것을 변형해서 사용하는 일

B의 경우 : 컴포넌트를 상속받지 않고 컴포넌트를 props 처럼 받아 쓰는 일

HOC를 사용하는 방법

사용하는 방법이라고 했지만 사실 만드는 방법에 가깝다. 어떻게 만드는지 알아보도록 하자.

  • HOC는 Cross-Cutting Concerns를 위해서 사용한다.

    cross-cutting concerns는 횡단 관심사를 말한다. 그렇다면 횡단 관심사란, 프로그램 로직이 진행 되는 동안 비슷한 일들을 묶 것을 말한다.

스크린샷 2020-12-23 오후 12 54 56

입금을 할때도, 출금, 이체를 할때도 모두 로깅, 보안, 트랜잭션을 거친다.

  • 오리지널 컴포넌트를 바꾸면 안된다.

    위에서 예시를 들었던 빼빼로를 변형시키면 안된다.

    즉, 인자로 받은 컴포넌트를 변형하지 않아야 한다.

  • Pass Unrelated Props Through to the Wrapped Component

    번역해보자면, 관련없는 props 들은 빼뺴로를 통과해야 한다.

    코드를 통해 이해해보자.

    function Pepero() {
      return <div>나는 뺴빼로 컴포넌트</div>
    }
    
    function withSnack(Component) {
      function Giftbox() {
    		return <Component />
      }
      return Giftbox;
    }
    
    export default withSnack(Pepero);

    이런 코드가 있다고 해보자. withSnack(Pepero)를 통해 우리는 다음과 같이 사용할 수 있다.

    <Pepero>아몬드맛 빼빼로</Pepero>

    <Pepero>라고 적었지만 이것은 빼뺴로 컴포넌트가 아니라 Giftbox 컴포넌트를 말한다. 따라서, 아몬드맛 빼빼로라고 한 props.children은 뺴뺴로 컴포넌트에 들어가지 않고 Giftbox의 인자로 받을 수 있다.

    function withSnack(Component) {
      function Giftbox(props) {
    		return <Component {...props} />
      }
      return Giftbox;
    }

    이렇게 빼빼로에게 props를 전달해줄 수 있다.

  • Maximizing Composability

    조합을 충분히 활용해라 = 상속 안쓰면 된다.

  • Wrap the Display Name for Easy Debugging

    쉬운 디버깅을 위해서 컴포넌트 display name 을 붙여줘야 된다.

    function withSnack(Component) {
      function Giftbox(props) {
    		return <Component {...props} />
      }
      
      Giftbox.displayName = `${Component.displayName} => withSnack 컴포넌트 사용했다고 표현`
      
      return Giftbox;
    }

    이렇게 해주면 크롬 확장 프로그램인 React dev tools로 볼때 디버깅 하기 쉽다.

HOC 주의할 점

  1. render 메서드에 HOC를 사용하면 안된다.
render() {
	const NewComponent = withSnack(Pepero);
  
  return <NewComponent />
}

이런식으로 작성하게 되면 렌더링될 때마다 NewComponent가 생성된다. 이는 성능 문제를 일으키기도 하고, 또 컴포넌트가 리마운트되면 해당 컴포넌트와 자식 컴포넌트의 상태 또한 잃어버리게 된다.

  1. static 메서드는 반드시 복사해야 한다.

빼빼로에 static 메서드가 있다면, 그 static 메서드가 자동으로 NewComponent에 생기지 않는다.

Pepero.test = () => {
  // 어쩌구 저쩌구
};

그렇다면 static 메서드를 따로 연결해주는 작업이 필요하다.

function withSnack(Pepero) {
	class Giftbox extends React.Component {/.../}

	Giftbox.staticMethod = Pepero.staticMethod

	return Giftbsox;
}

너무 귀찮은 작업이다. 그래서 hoist none react statics 라이브러리의 도움을 받는다.

import hoistNonReactStatics from 'hoist-none-react-statics';

function withSnack(Pepero) {
	class Giftbox extends React.Component {/.../}

	hoistNonReactStatics(Giftbox, Pepero);

	return Giftbsox;
}
profile
프론트엔드 개발자가 되기 위해 공부 중입니다.

0개의 댓글