티스토리 뷰

고차컴포넌트

컴포넌트 logic을 재사용하기 위한 React의 고급 기술이다.

React API가 아닌 리액트의 특성에서 나오는 패턴이다.

고차 컴포넌트는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수이다.

컴포넌트는 props를 UI로 변환하는 한편, 고차 컴포넌트는 새로운 컴포넌트로 변환한다.

고차 컴포넌트는 Redux의 connect와 Relay의 createFragmentContainer와 같은 서드 파티 React library에서 흔하게 볼 수 있다.

횡단 관심사(Cross-Cutting Concerns)에 고차 컴포넌트 사용하기

만약 외부로 부터 데이터를 subscribe하여 댓글 목록을 Rendering하는 컴포넌트가 있다고 하자.

그리고 블로그의 Post를 구독하기 위해 비슷한 패턴의 컴포넌트가 있다고 하자.

이 2개의 컴포넌트는 동일하지 않고 다른 Rendering 결과를 보여주지만 대부분의 구현체가 동일하다.

만약 Application의 규모가 커질수록 비슷한 패턴의 컴포넌트를 한곳에서 정의하고 공유할 수 있도록 추상화가 필요하다. 이러한 경우 고차 컴포넌트를 사용하면 좋다.

고차 컴포넌트는 입력된 컴포넌트를 수정하지 않아야 하며, 상속을 사용하여 동작을 복사하지도 않는다.

고차 컴포넌트는 원본 컴포넌트를 컨테이너 컴포넌트로 포장하여 조합한다. 

고차 컴포넌트를 구성할 때 side effect가 전혀 없는 순수함수로 작성하여야 한다,

컴포넌트 조합(Component Composition)

고차 컴포넌트 내부에서 컴포넌트의 프로토타입을 수정(또는 변경)하지 않도록 한다.

function logProps(InputComponent) {
  InputComponent.prototype.componentDidUpdate = function(prevProps) {
    console.log('Current props: ', this.props);
    console.log('Previous props: ', prevProps);
  };
  //원본의 입력을 반환 -> 상태의 변형
  return InputComponent;
}

//props를 받을때 마다 log를 남김
const EnhancedComponent = logProps(InputComponent);

 

위의 코드에서 몇가지 문제가 있다.

그 중 하나는 입력된 컴포넌트를 EnhancedComponent와 별도로 재사용 할 수 없다는 점이다.

componentDidUpdate를 변형하는 EnhancedComponent에 또 다른 HOC를 적용하면 첫 번째 HOC의 기능은 무시된다.

이 HOC는 생명주기 메서드가 없는 함수컴포넌트에서도 작동하지 않는다.

변경된 HOC는 누출된 추상화(leaky abstraction)이다. Consumner는 다른 HOC와의 충돌을 피하기 위해 어떻게 구현되어 있는지 반드시 알아야 한다.

HOC는 변경 대신 입력 컴포넌트를 컨테이너 구성요소로 감싸서 조합(composition)해야 한다.

function logProps(WrappedComponent) {
  return class extends React.Component {
    componentDidUpdate(prevProps) {
      console.log('Current props: ', this.props);
      console.log('Previous props: ', prevProps);
    }
    render() {
      return <WrappedComponent {...this.props} />;
    }
  }
}

위의 HOC는 충돌 가능성을 피하면서 프로토타입을 직접 변경한다.

순수한 함수이므로 다른 HOC와 같이 조합하거나, 자체적으로 조합하는것도 가능하다.

Wrapping된 컴포넌트를 통하여 관련없는 Props전달

HOC는 컴포넌트에 기능을 추가한다. HOC는 정의를 과감하게 변경하면 안된다.

HOC에서 반환된 컴포넌트는 래핑된 컴포넌트와 비슷한 interface가 있어야 한다.

HOC는 특정 관심사와 관련이 없는 props를 활용하여야 한다.

대부분의 고차 컴포넌트에는 다음과 같은 rendering method가 포함되어 있다.

이러한 컨벤션은 HOC의 유연성과 재사용성을 보장하는데 도움이 된다.

render() {
  // 이 HOC에만 해당되므로 추가된 props는 걸러내어 이 HOC에 전달되지 않도록 합니다.
  const { extraProp, ...passThroughProps } = this.props;

  // 이 Props는 일반적으로 Status값 또는 Instance method 입니다.
  const injectedProp = someStateOrInstanceMethod;

  // wrapped component에 props를 전달합니다.
  return (
    <WrappedComponent
      injectedProp={injectedProp}
      {...passThroughProps}
    />
  );
}

주의 사항

render method 내에서 HOC를 사용하면 안된다.

재조정(reconciliation)으로 알려진 React의 비교 알고리즘은 컴포넌트의 개별성(identity)을 가지고 기존 서브트리를 업데이트 해야 하는지 아니면 버리고 새로운 node를 마운트 해야할 지 결정한다.

1. render에서 반환 된 컴포넌트가 이전에 rendering 된 component와 비교하여,

2 - 1. 동일하다면 React가 새로운 서브트리와 비교하여 재귀적으로 서브트리를 업데이트 한다.

2 - 2. 동일하지 않다면 이전 서브트리는 완전히 마운트 해제된다.

일반적으로 위 냉ㅇ에 대해 생각할 필요는 없다. 하지만 컴포넌트의 render method안에서 고차 컴포넌트를 사용할 수 없기 때문에 고차 컴포넌트를 사용할 때 위 내용을 짚고 넘어가야 한다.

render() {
  // render가 호출될 때마다 새로운 버전의 EnhancedComponent가 생성됨
  // 기존 EnhancedComponent !== 새로운 EnhancedComponent
  const EnhancedComponent = enhance(MyComponent);
  // 때문에 매번 전체 서브트리가 마운트 해제 후 다시 마운트 된다
  return <EnhancedComponent />;
}

성능상의 문제가 생길 뿐만아니라 component가 다시 mount되면서 기존 state와 컴포넌트 하위 항목들이 손실된다

대신에 컴포넌트의 정의 바깥에 HOC를 적용하여 컴포넌트가 한 번만 생성되도록 합니다. 그러면 해당 component는 여러번 렌더링이 되더라도 일관성을 유지합니다. 일반적으로 렌더링이 여러번 되어도 바뀌길 원하는 사람은 없을 것이라고 생각합니다.

 

정적 method는 반드시 따로 복사

 

Ref는 전달되지 않는다.

HOC는 모든 props를 wrapping된 component에 전달하는 것이 원칙이지만, refs에서는 작동되지 않는다.

React에서 ref는 실제 props가 아닌 key와 같이 특별하게 취급되기 때문이다.

HOC의 결과인 element에 ref를 추가하는 경우 ref는 Wrapping된 컴포넌트가 아닌 가장 바깥쪽 컨테이너 컴포넌트의 인스턴스를 나타냄

이를 해결하기 위해 React.forwardRef API를 사용할 수 있다.

 

참고 : ko.reactjs.org/docs/higher-order-components.html

 

고차 컴포넌트 – React

A JavaScript library for building user interfaces

ko.reactjs.org

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함