TIL 22-04-08

thisisyjin·2022년 4월 8일
0

TIL 📝

목록 보기
15/113

React.js

Today I Learned ... react.js

🙋‍♂️ React.js Lecture

🙋‍ My Dev Blog


다중 하이어오더 컴포넌트

  • 하이어오더 컴포넌트는 구조상 다른 하이어오더 컴포넌트를 조합할 수 있음.
    -> 예) Page 컴포넌트에 서버 데이터 호출 기능과 로딩메시지 출력 기능이 필요하면
    -> 각 하이어오더 컴포넌트 생성함수 (withstate, lifecycle 등)을 조합함.
  • 반복된 코드를 잘게 분리하여 공유 -> 코드의 양을 줄일 수 있음.

'다중 하이어오더 컴포넌트' 란?

  • 하이어오더 컴포넌트란, 기존 컴포넌트에 기능을 추가하여 새로운 컴포넌트를 반환하는 구조이므로,
    하이어오더 컴포넌트를 겹치면 계속해서 컴포넌트의 기능을 확장 가능.
  • 여러 하이어오더 컴포넌트를 이어서 호출
function Component() {
  return null;
}

const ComponentWithZ = withZ(Component);
const ComponentWithYandZ = withY(ComponentWithZ);
const ComponentWithXandYandZ = withZ(ComponentWithYandZ);
// 🔺 withX(withY(withZ(Component)와 같음.

compose()

  • recompose 라이브러리에서 제공하는 compose() 함수
  • 지난번에 직접 이중커링으로 구현했던 compose()함수와 같은 기능을 함.

직접 작성했던 compose 함수

function compose() {
    const funcArr = Array.prototype.slice.call(arguments); // 유사배열 객체(arguments)를 배열로 만듬
    return funcArr.reduce(   // reduce함수를 반환함 - 첫번째 인수로는 각 요소(여기선 함수)를 반복할 콜백 전달
        function (prevFunc, nextFunc) {   // 커링 ( ✔️ 함수를 조합하는 함수를 리턴함 )
            return function (value) {
                return nextFunc(prevFunc(value));
            }
        },
        function (k) { return k; }  // reduce의 두번째 인수로는 초기값을 설정. (맨처음 조합되는 함수)
    );
}

const formulaWithCompose = compose(multiplyTwo, multiplyThree, addFour);
  • compose 함수 임포트
import compose from 'recompose/compose';
const withXYZ = compose(withX, withY, withZ);
const ComponentWithXYZ = withXYZ(Component);
// 🔺 const ComponentWithXYZ = compose(withX, withY, withZ)(Component);

compose()로 다중 하이어오더 컴포넌트 사용

/src/component/compose.jsx

import compose from 'recompose/compose';
import withLoading from './withLoading'; // 하이어오더 컴포넌트 생성함수
import withState from 'recompose/withState';
const withLoadData = withState('isLoading'<, 'setIsLoading', false); // withState(state, setState, state초기값)

function Component() {
  return '완료(컴포넌트 출력)';
}

const ComponentWithLoading = withLoading('로딩 중')(Component); // withLoading의 인수로 loadingMsg를 전달
const ComponentWithLoadData = withLoadData(ComponentWithLoading); // withState(..)(ComponentWithLoading) -> withLoading에서 반환한 하이어오더컴포넌트를 인수로 전달


const withLoadDataAndLoading = compose(withLoadData, withLoading('로딩 중')); // 순서 주의
// withLoadData(withState)를 먼저 정의한 후에, isLoading 프로퍼티를 withLoading에 전달해야함.

const ComponentWithLoadData = withLoadDataAndLoading(ComponentWithLoading);
// 펼쳐보면 아래와 같다.
withLoadData(withLoading('로딩 중')(Component))
// 다시 말해, 아래와 같다.
withState('isLoading', 'setIsLoading' false)(withLoading('로딩 중')(Component))

// ❖ 여기서 withLoading은 branch, 즉 props중 isLoading이 true, false인지에 따라 컴포넌트를 출력.
// 즉, withLoading은 커링'함수' 이다.
// 반면, withLoadData는 (=withState의 반환값) 하이어오더 컴포넌트이다.
  • compose()의 인자에는 같은 요소가 들어가야 한다.
    -> withLoading은 커링함수이고, withLoadData는 하이어오더 컴포넌트이다. -> 다른 요소임.
    BUT. withLoading('로딩 중')이 반환한 값이 하이어오더 컴포넌트이므로 조합 가능.

❌❌❌❌❌❌❌❌ 위에 부분 이해 안됨

  • withState()와 lifecycle() 안에 state는 기존 컴포넌트에 프로퍼티로 전달됨.

withLoading + lifecycle 예제(서버요청)

import React from 'react';
import lifecycle from 'recompose/lifecycle';
import compose from 'recompose/compose';
import withLoading from './withLoading';

function Page({ content }) {
  return (
    <div>
      페이지 로딩이 완료되었습니다.
      {content}
    </div>
  );
}

export const withLoadData = lifecycle({
  state: { isLoading: true, content: '' },
  componentDidMount: function () {
    if (this.props.loadData) {
      this.props.loadData().then((content) =>
        this.setState({
          isLoading: false,
          content,
        })
      );
    }
  },
});

export const PageWithLoadData = withLoadData(Page);
export const PageWithLoadDataAndLoading = compose(
  withLoadData,
  withLoading('서버 요청중')
)(Page);

스토리북에 추가

  • 기존 LifecycleStory.jsx 수정.
import React from 'react';
import { storiesOf } from '@storybook/react';

import {
  PageWithLoadData,
  PageWithLoadDataAndLoading,
} from '../component/lifecycle';

storiesOf('LifeCycle', module)
  .add('loadData 예제', () => (
    <PageWithLoadData loadData={() => fetch('/').then(() => 'hello')} />
  ))
  .add('로딩 메시지 예제', () => (
    <PageWithLoadDataAndLoading
      loadData={() => fetch('/').then(() => 'hello')}
    />
  ));

(너무 빨라서 잘 안보이지만, '서버요청중' 이라고 로딩메시지가 뜬다!)

하이어오더 컴포넌트 복습

필수 입력 항목 표시 기능 컴포넌트

  • 입력 컴포넌트에 오류메시지를 출력하는 기능을 추가한 하이어오더 컴포넌트

/src/component/withError.jsx

import React from 'react';
import withStyles, { css } from '../component/withStyles';

export default function (defaultMsg) {  // 오류메시지를 커링함수의 인자로 전달
  return (WrappedComponent) => {  // WrappedComponent를 인수로 받음
    const { displayName, name: componentName } = WrappedComponent;   // 컴포넌트 이름 표시
    const wrappedComponentName = displayName || componentName;   // 기본값 설정

    function ComponentWithError({ hasError, errorMsg, styles, ...props }) {  // 확장 컴포넌트. 
      // (hasError 등 WrappedComponent에 props로 전달할 필요가 없는 것은 제외)
      return (
        <>
          <WrappedComponent {...props} />  
          {hasError && <div {...css(styles.error)}>{errorMsg}</div>} { // hasError가 true면 에러메시지 출력 (에러메시지는 styles로 꾸밈)}
        </>
      );
    }

    ComponentWithError.defaultProps = {
      errorMsg: defaultMsg,   //  커링함수의 인자로 받은 defaultMsg를 - props인 errorMsg에 할당.
    };

    ComponentWithError.displayName = `withError(${wrappedComponentName})`; 
          
    return withStyles(({ color }) => ({ // style.color을 구조분해로 
      error: {
        color: color.error,
      },
    }))(ComponentWithError);
  };
}
profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글