HoC 를 만드는 함수 만들기

이제 Context API 에 좀 익숙해졌지요? 한가지만 더 해봅시다. 우리가 Context 를 만들 때 마다 HoC 를 작성하는게 귀찮을 수도 있습니다. 또, 같은 Context 를 사용하지만 상황에 따라 다른 props 를 전달해줘야 할 때도 있을 것입니다. 그러한 상황에 도움을 줄 수 있는 함수를 새로 만들어보겠습니다.

src/lib/createUseConsumer.js

import React from 'react';

const createUseConsumer = (Consumer) => (mapContextToProps) => (WrappedComponent) => {
  // mapContextToProps 가 없으면 그냥 context 를 그대로 props 에 전달
  const defaultMapContextToProps = (context) => ({context});

  function UseConsumer(props) {
    return (
      <Consumer>
      {
        context => {
          // context에서 원하는 값 추출
          const contextProps = (mapContextToProps || defaultMapContextToProps)(context);
          return (
            <WrappedComponent
              {...contextProps}
              {...props}
            />
          );
        }
      }
      </Consumer>
    );
  }
  // displayName 설정
  const displayName = WrappedComponent.displayName || WrappedComponent.name || 'component';
  UseConsumer.displayName = `UseConsumer(${displayName})`;
  return UseConsumer;
}

export default createUseConsumer;

그럼, another Context 파일을 다음과 같이 작성해줄 수 있습니다.

src/contexts/another.js

import React, { Component, createContext } from 'react';
import createUseConsumer from '../lib/createUseConsumer';

const Context = createContext();

const {
  Provider,
  Consumer: AnotherConsumer
} = Context;

class AnotherProvider extends Component {
  state = {
    number: 1,
  }
  actions = {
    increment: () => {
      this.setState(
        ({ number }) => ({ number: number + 1 })
      );
    }
  }
  render() {
    const { state, actions } = this;
    const value = { state, actions };
    return (
      <Provider value={value}>
        {this.props.children}
      </Provider>
    );
  }
}

const useAnother = createUseConsumer(AnotherConsumer);

export {
  AnotherProvider,
  AnotherConsumer,
  useAnother
};

이제 useAnother 함수에서는 어떤 값을 props 에 넣어줄 지 정해주는 mapContextToProps 를 필요로 하니, Counter 컴포넌트도 조금 수정해주세요.

src/components/Counter.js

import React from 'react';
import { useAnother } from '../contexts/another';

const Counter = ({ number, increment}) => {
  return (
    <div>
      <h1>{number}</h1>
      <button onClick={increment}>더하기</button>
    </div>
  );
};

export default useAnother(
  ({ state, actions }) => ({
    number: state.number,
    increment: actions.increment
  })
)(Counter);

코드를 저장하고, 기존의 카운터가 제대로 작동하는지 확인해보세요.

results matching ""

    No results matching ""