react hoc , context api , recompose

반응형

react hoc , context api , recompose

https://velopert.com/3606

https://velopert.com/3537

https://github.com/acdlite/recompose

https://reactjs.org/docs/higher-order-components.html

리액트 에서 가장 기본이 되는 부분이다. 의외로 내용이 좀 난이도가 있기 때문에 많은 개발자들이 간과하고 깊이 있게 읽어보지 않고 넘어간다. 그로 인해 계속하여 소 귀에 경 읽기가 된다. 리덕스니 사이드 이펙트니 미들웨어니 인증이니 스마트 더미 컴포넌트 니 하는 많은 내용들이 있지만 정작 실전 코드에서 고차함수, 고차컴포넌트, 컨텍스트 api 개념을 모르면 코드를 이해하지 못한다. 왜냐하면 대다수의 유명한 리덕스 를 비롯한 styled-component, theme, intl 등등 에서 이를 사용하기 때문이다.

이 부분에 사용된 것이 High Order Component 라고 할 수 있다. 즉, 고차 함수, 고차 컴포넌트이다. 한마디로 컴포넌트를 함수로 또는 컴포넌트 로 한 번 감싼 것.

const RestrictedRoute = ({ component: Component, isLoggedIn, ...rest }) => (
  <Route
    {...rest}
    render={props =>
      isLoggedIn ? (
        <Component {...props} />
      ) : (
        <Redirect
          to={{
            pathname: "/signin",
            state: { from: props.location }
          }}
        />
      )
    }
  />
);



이런식으로 더 자주 사용된 것을 볼 수 있다. 함수에서 또 다른 함수를 리턴하도록 함. (url, WrappedComponent) 형식이 아니라, (url) => (WrappedComponent) 로 한 이유는, 나중에 여러개의 HOC 를 합쳐서 사용하게 될 때 더욱 편하게 사용하기 위함이다. – compose 같은 함수를 통하여 호출을 간소화 할 수 있다고 한다.

import React, { Component } from 'react';

const withRequest = (url) => (WrappedComponent) => {
  return class extends Component {
    render() {
      return (
        <WrappedComponent {...this.props}/>
      )
    }
  }
}

export default withRequest;



아래의 경우가 LocaleProvider, IntlProvider, ThemeProvider, DashAppHolder 등등 쭉 내려간다. 이것들이 모두 Context API 를 사용하여 만든 것이다. Context API 는 개념적으로 자체 구현되어 왔으나 16.3 버전부터 이를 편리하게 사용할 수 있는 createContext, Provider, Consumer 를 제공한다. 또한 withDirection, connect 함수들로 감싸여져 export default 되는 컴포넌트 들을 많이 보았을 텐데 이 또한 Context API , HOC 를 사용하여 만든 라이브러리 의 HOC 형식의 메소드이다.

const currentAppLocale =
  AppLocale[getCurrentLanguage(config.defaultLanguage || 'english').locale];

const DashApp = () => (
  <LocaleProvider locale={currentAppLocale.antd}>
    <IntlProvider
      locale={currentAppLocale.locale}
      messages={currentAppLocale.messages}
    >
      <ThemeProvider theme={themes[themeConfig.theme]}>
        <DashAppHolder>
          <Provider store={store}>
            <PublicRoutes history={history} />
          </Provider>
        </DashAppHolder>
      </ThemeProvider>
    </IntlProvider>
  </LocaleProvider>
);

Boot()
  .then(() => DashApp())
  .catch(error => console.error(error));

 

Context Api 보충

 

redux 를 사용하면 사실 자체적으로 Context Api 이용하여 로직을 만들어 적용할 이유가 거의 없다. 앱 전역의 로딩 모달이라던가 왼쪽 메뉴 오픈 등의 기능을 만들 때 사용하면 좋지만 redux 를 사용한다면 이 또한 redux 의 상태를 이용하여 구현하면 된다. 그것이 redux 의 목적이니깐... 오히려 Context Api 로 자체적으로 추가할 경우 소스를 더럽게 만든다. 또한 redux-persist 같은 것을 적용할 때 일관성 또한 떨어뜨릴 수 있다. 하지만 redux 또한 Context Api 로 구현되어 있다. 따라서 그 원리 및 사용법을 구체적으로 알고 있음이 좋다.

 

1차적으로 매우 단순하게 사용할 수 있다. (https://stackoverflow.com/questions/58193424/passing-state-with-usecontext-in-typescript) 하지만 이렇게 사용하는 것은 추천하지 않는다. 그냥 단순히 Context 의 상태를 object 형태로 하고 해당 상태를 immutable 하게 set 하는 메소드로 되어 있는 구조.

 

2차적으로 useReducer 훅을 사용하여 리듀서 구조로 object 의 상태를 분할하여 적용하고 단순히 useContext 를 사용하는 것이 아니라 커스텀 훅을 만들어서 사용하는 방법이 있다. 1차와 같이 한개의 object 에 모든 상태가 들어 있으면 이 object 가 변경될 때 해당 object 를 useContext 로 가져와서 사용하는 이 context 에 의존하는 모든 컴포넌트들이 리렌더링 될 것이다. object 안의 한 개의 값만 변경되는데도 object 전체를 set 해야한다. {isLoading, isLeftMenuOpen, isRightMenuOpen} 과 같이 있을 때  isLoading 만 변경하고 싶어도 이 object 전체를 다시 set 해야한다. 따라서 리듀서 구조로 해주는 것이 좋다. (https://velog.io/@velopert/typescript-context-api) (https://react.vlpt.us/using-typescript/04-ts-context.html)

 

하지만 결국 redux 를 그냥 사용하는 것이 좋다. 전역 state 인 store 는 하나이어야 하고 가장 유명한 것이 redux 이다! redux-toolkit 으로 인해 best practice 까지 제공하지 않는가. isLoading, isLeftMenuOpen, isRightMenuOpen 과 같은 것을 구현할 때 isLoading 이 전역의 앱 전체의 로딩 모달을 보여주는 플래그라 할 경우 각각의 Thunk 액션의 loading state 들을 useSelect 하여 || 연산자로 하는 식으로 구현하는 것이 가장 좋아보인다.

 

그리고 React 의 portal 이라는 것의 용도와 조금 혼동될 수 있는데 portal 은 다이알로그 메시지를 띄워주기 위한 용도 같은 경우에 사용하는 것이다. context api 는 Provider 로 감싸주어서 순차적으로 자식에 자식으로 property 를 넘기는 것을 건너 뛰고 편하게 그리고 해당 component 에서만 변화에 반응하도록 하는 기술로서 전역적인 상태 관리 용도라고 한다치면 portal 은 전역적인 어떤 지점에 요소 (DOM 이라던가 React Native 라면 어떤 부분의 컴포넌트) 를 id 라던가 ref 로 기준을 잡아놓고 createPortal 을 이용해서 해당 부분에 원하는 내용을 집어넣어줄 수 있는 기술이다. 사실 ReactDOM.createPortal 이라는 메소드로서 React Native 에서는 이러한 기술이 없다. (https://m.blog.naver.com/psj9102/222141597022) html 엘리먼트야 id 같은 것으로 select 할 수 있지만 리액트 네이티브는 그런식의 요소 선택이 어렵기 때문이다.

 

 

 

 

 

 

 

 

 

반응형

댓글

Designed by JB FACTORY