Today I Learned ... react.js
🙋♂️ React.js Lecture
🙋 My Dev Blog
하이어오더 컴포넌트
란, 기존 컴포넌트에 기능을 추가하여 새로운 컴포넌트를 반환하는 구조이므로,function Component() {
return null;
}
const ComponentWithZ = withZ(Component);
const ComponentWithYandZ = withY(ComponentWithZ);
const ComponentWithXandYandZ = withZ(ComponentWithYandZ);
// 🔺 withX(withY(withZ(Component)와 같음.
직접 작성했던 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);
- 자세한 설명은 이전 포스팅 참조.
import compose from 'recompose/compose';
const withXYZ = compose(withX, withY, withZ);
const ComponentWithXYZ = withXYZ(Component);
// 🔺 const ComponentWithXYZ = compose(withX, withY, withZ)(Component);
/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의 반환값) 하이어오더 컴포넌트이다.
❌❌❌❌❌❌❌❌ 위에 부분 이해 안됨
state
는 기존 컴포넌트에 프로퍼티로 전달됨.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);
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);
};
}