TIL 22-04-07

thisisyjin·2022년 4월 8일
0

TIL 📝

목록 보기
14/113

React.js

Today I Learned ... react.js

🙋‍♂️ React.js Lecture

🙋‍ My Dev Blog


하이어오더 컴포넌트 라이브러리

Do IT! React - CH5. 하이어오더 컴포넌트

Recompose 라이브러리

1. 설치 후 import

  • yarn 설치 (--dev 아님)
> yarn add recompose
  • 임포트 - 모든 함수 임포트하기보다는 원하는 함수만 추출하여 임포트하기.
// ❌ 비효율적 - 프로젝트가 무거워짐
import { branch, withState } from 'recompose';

// 💡 효율적
import branch from 'recompose/branch';
import withState from 'recompose/withState';

branch()

✅ branch() 함수

  • 조건에 따라 다른 컴포넌트 출력
branch(
  condition: props => boolean,
  left: HigherOrderComponent,
  right: HigherOrderComponent
  )(WrappedComponent)
  • condition에 출력조건을 정의한 함수가 들어가고,
  • left는 참일때 출력될 컴포넌트,
  • right에는 거짓일 때 출력될 컴포넌트가 들어감. (right는 생략가능)

2. branch()함수로 로딩상태 하이어오더 컴포넌트 생성

/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);

3. 스토리북에 추가

/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()

  • 함수형 컴포넌트는 state를 사용할 수 없지만, 프로퍼티와 콜백을 활용해 우회적으로 사용 가능함.
  • withState()는 함수형 컴포넌트도 state를 바로 사용 가능하게 해줌.
    (this.state나 this.setState 없이도 state 변경 가능)
withState(
  stateName: string,  // 전달할 프로퍼티 이름
  stateUpdater: string,  // state를 변경할 수 있는 콜백 함수 프로퍼티명
  initialState: any,  // state의 초기값
  )(WrappedComponent)

1. withState() 함수로 카운트 관리 state 구현

/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);

2. Counter 컴포넌트 재구성

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>
  );
}
  • 기존에 state를 쓰던 Counter과 비교해보자!
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 해줌.
  • withState() 함수에서 정의한 count와 setCount를 프로퍼티로 받아 사용함.
  • setCount의 인자로는 withState()함수의 첫번째 인자인 count를 조절할 함수를 전달함.
  • 변경 콜백 함수(setCount)는 값을 인자로 직접 전달해도 되고, 함수를 인자로 전달하여 state값에 접근해서 변경해도 된다.
setCount(0);  // 1) 값을 인자로 전달 - count를 0으로 설정
setCount(c => c + 1);  // 2) 함수를 인자로 전달 - state(=count)에 접근하여 변경
setCount(c => c * 10);

3. Counter 컴포넌트를 withCountState()에 전달하여 export

...
export const CounterWithCountState = withCountState(Counter);
  • default가 아닌 CounterWithCountState 라는 이름으로 (export const) 익스포트 함.
withState('count', 'setCount', 0)(Counter) // 위 코드는 이와 같음. 
// count는 Counter의 props로 변환되어 전달되는 것

4. 스토리북에 추가

import React from 'react';
import { storiesOf } from '@storybook/react';

import { CounterWithCountState } from '../component/withState';

storiesOf('WithState', module).add('CounterWithCountState', () => (
  <CounterWithCountState />
));

lifecycle()

  • 함수형 컴포넌트에도 생명주기 함수를 적용하고 싶을 때 사용.
  • lifecycle()을 통해 우회적으로 함수형 컴포넌트에 생명주기 함수를 추가 가능.
  • 클래스형 컴포넌트의 경우, 생명주기 함수에서 쓸 반복 코드를 lifecycle() 함수로 묶어 재사용 가능.
lifecycle({
  [lifeCycleMethod: string]: function,    // 함수 이름(componentDidUpdate 등)
  state: Object,    // state의 초기값
})(WrappedComponent)
  • lifecycle 함수는 this.props에 접근 가능 -> 커링을 통해 따로 props를 전달받을 필요 X
  • BUT. 화살표 함수 사용 불가.
    (화살표 함수는 this의 범위가 선언된 구문의 범위로 고정되므로)
  • 함수 내부에는 생명주기함수 이름과 state초기값 설정 가능.
  • setState()로 설정된 state값은 WrappedComponent에 props로 변환되어 전달됨.

1. lifecycle()함수로 최초 생성시 데이터 요청

/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 컴포넌트에 적용.
  • 여기서 lifecycle에서의 state가 (setState()로 바뀐 state) Page 컴포넌트에 프로퍼티로 전달되는 것.
  • 기존의 서버 데이터를 호출하는 예제임. (loadData와 then)

2. 스토리북에 추가

import React from 'react';
import { storiesOf } from '@storybook/react';

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

storiesOf('LifeCycle', module).add('loadData 예제', () => (
  <PageWithLoadData loadData={() => fetch('/').then(() => 'hello')} />
));
  • fetch() 함수로 서버데이터 호출.
  • 서버데이터 호출 완료시 - 'hello' 를 반환하여 (lifecycle.jsx의) loadData() 함수의 then함수 인자로 전달.

🔻 dev tool - [Network] 탭 (localhost로 서버요청이 요청됨)


정리

recompose 라이브러리

  • recompose는 반복해서 입력해야하는 경우에 사용. (코드의 양을 줄여야 할 때)
  • 적절할 때 최소한으로 사용하는 것이 좋음.

branch

  • 조건에 따라 다른 컴포넌트 출력

withState

  • 함수형 컴포넌트에서도 state 사용 가능 (props, callback 이용 -> 우회적으로)

lifecycle

  • 함수형 컴포넌트에서도 lifecycle method 사용 가능
profile
기억은 한계가 있지만, 기록은 한계가 없다.

0개의 댓글