Today I Learned ... react.js
🙋♂️ React.js Lecture
🙋 My Dev Blog
Do IT! React - CH5. 하이어오더 컴포넌트
> yarn add recompose
// ❌ 비효율적 - 프로젝트가 무거워짐
import { branch, withState } from 'recompose';
// 💡 효율적
import branch from 'recompose/branch';
import withState from 'recompose/withState';
✅ branch() 함수
- 조건에 따라 다른 컴포넌트 출력
branch( condition: props => boolean, left: HigherOrderComponent, right: HigherOrderComponent )(WrappedComponent)
condition
에 출력조건을 정의한 함수가 들어가고,left
는 참일때 출력될 컴포넌트,right
에는 거짓일 때 출력될 컴포넌트가 들어감. (right는 생략가능)
/src/component/branch.jsx
import React from 'react';
import branch from 'recompose/branch';
import Button from '../component/Button';
function isLoading(props) {
return props.isLoading;
}
function LoadingButton(props) {
return <Button disabled>로딩 중</Button>
}
export default branch(
isLoading, // isLoading이 참이면 - props.isLoading을 반환 (로딩 메시지)
() => LoadingButton, // 로딩 완료시 - LoadingButton 컴포넌트를 반환
)(Button);
간략하게 표현하면?
import React from 'react';
import branch from 'recompose/branch';
import Button from '../component/Button';
export default branch(
({ isLoading }) => isLoading, // (props)를 구조분해할당 -> 리턴하는 값(=isLoading)이 props.isLoading가 됨.
() => () => <Button disabled>로딩 중</Button> // LoadingButton을 화살표함수식으로 표현
)(Button);
/src/stories/BranchStory.jsx
import React from 'react';
import { storiesOf } from '@storybook/react';
import BranchLoaingButton from '../component/branch';
storiesOf('Branch', module)
.add('기본 설정', () => <BranchLoaingButton>안녕하세요</BranchLoaingButton>)
.add('isLoading 예제', () => (
<BranchLoaingButton isLoading>안녕하세요</BranchLoaingButton>
));
> npm run storybook
withState(
stateName: string, // 전달할 프로퍼티 이름
stateUpdater: string, // state를 변경할 수 있는 콜백 함수 프로퍼티명
initialState: any, // state의 초기값
)(WrappedComponent)
/src/component/withState.jsx
import React from 'react';
import withState from 'recompose/withState';
import Button from '../component/Button';
export const withCountState = withState('count', 'setCount', 0);
import React from 'react';
import withState from 'recompose/withState';
import Button from '../component/Button';
export const withCountState = withState('count', 'setCount', 0);
function Counter({ count, setCount }) {
const increaseCount = () => setCount((value) => value + 1);
return (
<div>
현재 카운트 : {count}
<Button onPress={increaseCount}>카운트 증가</Button>
</div>
);
}
import React from "react";
class NewCounter extends React.Component{
constructor(props) {
super(props);
this.state = {};
this.increaseCount = this.increaseCount.bind(this);
}
static getDerivedStateFromProps(props, state) { //
const { count } = props; // props로 부모의 state인 count를 받고,
return {
count,
newCount: count === state.count ? state.newCount : count, // 자신의 state로 할당
};
}
increaseCount() {
this.setState(({ newCount }) => ({
newCount: newCount + 1, // 자신의 state로 등록한 newCount 조작 (setState)
}));
}
render() {
return (
<div>
현재 카운트 : {this.state.newCount}
<button onClick={this.increaseCount}>카운트 증가</button>
</div>
);
}
}
export default NewCounter;
📌 static getDerivedStateFormProps(props, state)
getDerivedStateFormProps() 함수는정적 함수
이다.
-> this.props나 this.state로 접근 못함.
- 인자로 전달된 props, state를 사용해야 함.
- 상위 컴포넌트에서 받은 props로 state값을 연동할 때 주로 사용.
- return값으로 setState 해줌.
count
를 조절할 함수를 전달함.setCount(0); // 1) 값을 인자로 전달 - count를 0으로 설정
setCount(c => c + 1); // 2) 함수를 인자로 전달 - state(=count)에 접근하여 변경
setCount(c => c * 10);
withCountState()
에 전달하여 export...
export const CounterWithCountState = withCountState(Counter);
withState('count', 'setCount', 0)(Counter) // 위 코드는 이와 같음.
// count는 Counter의 props로 변환되어 전달되는 것
import React from 'react';
import { storiesOf } from '@storybook/react';
import { CounterWithCountState } from '../component/withState';
storiesOf('WithState', module).add('CounterWithCountState', () => (
<CounterWithCountState />
));
lifecycle({
[lifeCycleMethod: string]: function, // 함수 이름(componentDidUpdate 등)
state: Object, // state의 초기값
})(WrappedComponent)
/src/component/lifecycle.jsx
import React from 'react';
import lifecycle from 'recompose/lifecycle';
function Page({ content }) { // lifecycle() 안에 있는 content를 프로퍼티(props)로 전달함
return (
<div>
페이지 로딩이 완료되었습니다.
{content}
</div>
);
}
export const withLoadData = lifecycle({
state: { isLoading: true, content: '' }, // state 초기값
componentDidMount: function () { // 초기 렌더링 완료시 (+ 화살표함수 못씀)
if (this.props.loadData) { // loadData 프로퍼티에 함수가 선언된 경우에만 실행되도록
this.props.loadData().then((content) => // 로딩 완료시(then) 에만 state가 바뀌게
this.setState({
isLoading: false,
content,
})
);
}
},
});
export const PageWithLoadData = withLoadData(Page); // lifecycle()로 만든 하이어오더 컴포넌트 생성 함수(withLoadData)를 Page 컴포넌트에 적용.
import React from 'react';
import { storiesOf } from '@storybook/react';
import { PageWithLoadData } from '../component/lifecycle';
storiesOf('LifeCycle', module).add('loadData 예제', () => (
<PageWithLoadData loadData={() => fetch('/').then(() => 'hello')} />
));
🔻 dev tool - [Network] 탭 (localhost로 서버요청이 요청됨)
branch
- 조건에 따라 다른 컴포넌트 출력
withState
- 함수형 컴포넌트에서도 state 사용 가능 (props, callback 이용 -> 우회적으로)
lifecycle
- 함수형 컴포넌트에서도 lifecycle method 사용 가능