React - HOC 대신 useContext를 사용해보자.

2021. 7. 7. 17:29프론트엔드/React.js

728x90

사실 나는 HOC를 활용한 컨텍스트만 사용했다.
왜냐면 그렇게 배워서...

그러다가 Hook 공식 문서를 읽어보고
'어? 그럼 Context도 Hook으로 할 수 있겠는데?'
생각을 해서 개발을 해보려는데

https://ko.reactjs.org/docs/hooks-reference.html#usecontext

 

Hooks API Reference – React

A JavaScript library for building user interfaces

ko.reactjs.org

useContext가 이미 있었다 ^___^

어후 안 읽어봤으면 또 개고생했을 뻔


HOC에 대해 알아보자

Context를 사용하는 방법은 다른 포스팅에서 알아보기로 하고,
HOC를 이용해서 컴포넌트를 Wrap하는 방법을 알아보자.

HOC는 Higher Order Component로, 말 그대로 상위 컴포넌트로 감싸는 방식이다.
예를 들어 이런 경우를 보자.

function App() {
  return (
    <div>
      <A />
      <B />
      <C />
    </div>
  )
}

이런 A, B, C라는 컴포넌트가 있을 때, 이 컴포넌트를 어떤 컴포넌트의 자식으로 내리고 싶다.
예를 들면, 이전에 했던 NullWrapper를 생각해보자.

function App() {
  return (
    <div>
      <NullWrapper>
        <A />
      </NullWrapper>
      <NullWrapper>
        <B />
      </NullWrapper>
      <NullWrapper>
        <C />
      </NullWrapper>
  );
}

너무 지저분하다..
아무튼 저렇게 다른 컴포넌트에 Wrap을 하게 되면, 코드를 보기도 힘들게 되고, 노가다도 심해진다.

그래서 감싸는 코드를 만드는 것이다.

function withNullWrapper(Component) {
  return (
    <NullWrapper>
      <Component />
    </NullWrapper>
  )
}

function App() {
  return (
    <div>
      {withNullWrapper(A)}
      {withNullWrapper(B)}
      {withNullWrapper(C)}
    </div>
  );
}

하지만 이것도 좀 보기 그럴 수 있다.
A라는 컴포넌트는 어디에서 사용되던지 NullWrapper에 감싸져야한다면,
매번 사용할때마다 withNullWrapper를 호출해야 한다.

그렇다면 A 자체를 Wrap하면 어떨까?

function withWrapper(Component) {
  return (
    <div style={{ backgroundColor: 'rgb(125,125,125)' }}>
      <Component />
    </div>
  )
}

function A() {
  return (
    withWrapper(() =><div>A 입니다</div>)
  )
}

function App() {
  return (
    <div>
      <A />
    </div>
  );
}

와! 너무너무 깔끔해요 ^____^
부모님 코드에 꼭 놔드려야겠어요~

여기서 한술 더 떠서 Component를 Wrap해서 export할 수도 있다.

fucntion A() {
  return (
    <>A 입니다</>
  );
}

export default withNullWrapper(A);

이러면 A 내부에 NullWrapper로 감싸지 않아도 다른 곳에서 사용할 때 자동으로 Wrap된다.


HOC를 이용한 Context 활용법

이 HOC를 이용해서 Context를 활용해보자.

// SurveyContext.tsx
const SurveyContext = React.createContext({});

function withSurveyContext(WrappedComponent: WrappedType) {
  return (props: any) => (
    <SurveyContext.Consumer>
      {obj => (obj
          ? <WrappedComponent {...obj} {...props} />
          : <div />
      )}
    </SurveyContext.Consumer>
  )
};

export default SurveyContext
export { withSurveyContext }

// App.tsx
function App() {
  return (
    <SurveyContext.Provider value={{ weight: 0 }}>
      <A />
    </SurveyContext.Provider>
  )
}

// A.tsx
function A(props) {
  const { weight } = props;
 
  return (
    <div>{weight}</div>
  );
}

export default withSurveyContext(A);

세개의 파일로 나눠봤다.
App.js나 index.js (컴포넌트 트리 최상위)에 Provider로 감싸주는걸 잊지 말고,
Context.Consumer를 Wrap하는 withSurveyContext를 export해준다.

그러면 이제 withSurveyContext에 감싸진 컴포넌트는,
<SurveyContext.Consumer>에 감싸진 효과를 얻게 된다.

최종 트리는 다음과 같다.

<SurveyContext.Provider value={{ weight: 0 }}>
  <SurveyContext.Consumer>
    {({ weight }) => <div>{weight}</div>}
  </SurveyContext.Consumer>
</SurveyContext.Provider>

Hook (useContext)를 이용해보자.

우선 useContext의 인자는 Context 그 자체다. Provider나 Consumer를 전달하지 않도록 조심하자.

const SurveyContext = React.createContext({});
 
...

const survey = useContext(SurveyContext);

이렇게 되면, Context.Provider에 전달된 value를 사용할 수 있다.

이 방법을 사용해서 위의 예제를 바꿔보자.

// SurveyContext.tsx
const SurveyContext = React.createContext({});

export default SurveyContext

// App.tsx
function App() {
  return (
    <SurveyContext.Provider value={{ weight: 0 }}>
      <A />
    </SurveyContext.Provider>
  )
}

// A.tsx
function A() {
  const survey = useContext(SurveyContext);
  const { weight } = survey;
 
  return (
    <div>{weight}</div>
  );
}

export default A;

... 뭐 그렇다. 끝났다.


참고 자료

https://ko.reactjs.org/docs/hooks-reference.html#usecontext

 

Hooks API Reference – React

A JavaScript library for building user interfaces

ko.reactjs.org

'프론트엔드 > React.js' 카테고리의 다른 글

React에서 배경을 깔쌈하게 넣어보자  (0) 2021.07.12
Javascript for 반복문 정리  (0) 2021.07.09
React Router 배워보기  (0) 2021.07.04
Material-UI Theme에 대해 알아보자  (0) 2021.07.03
React Hook에 대한 탐구  (0) 2021.07.03