패스트 캠퍼스 MGS 3기 - 5월 16일(React, 공식문서)

JY·2022년 5월 16일
0

1. Hooks


공식문서: https://ko.reactjs.org/docs/hooks-intro.html

클래스의 단점을 보완하면서 라이프사이클 등과 관련된 함수를 재사용 가능하게 한다.

사용 규칙
훅은 자바스크립트 함수지만 두 가지 규칙을 준수해야 한다.

  • 최상위에서 훅을 호출해야 한다. 반복문, 조건문, 함수 내에서 훅을 실행하면 안 된다.
  • 리액트 함수 내에서만 호출해야 한다. (함수 컴포넌트와 커스텀 훅) 클래스 컴포넌트에서는 사용할 수 없다.

State Hook


state를 관리하고 싶을 때 사용한다.

state 변수와 해당 변수를 갱신할 수 있는 함수를 반환한다.
대괄호? 배열에 구조 할당을 하는 것이다.

const [state, setState] = useState(0);

this.setState와 달리, 즉, 클래스 컴포넌트와 달리 state를 갱신하는 것은 병합(원래 있던 것에 추가)이 아니라 대체(원래 있던 것을 버림!)이다.

lazy initialization 이니셜 값을 고정으로 줄 수도 있지만 함수를 주어서 딜레이를 줄 수 있다.

정리


Effect Hook


주로 데이터 가져올 때, 구독 설정할 때, 수동으로 리액트 컴포넌트의 DOM을 수정할 때 사용한다.

이펙트가 업데이트 시마다 실행되는 이유
클래스 메소드가 관련없는 로직들은 모아놓고, 관련있는 로직들은 여러 개의 메소드로 나누어 놓는 경우가 있었다. (componentDidMount, componentDidUpdate, componentWillUnmount) 이에 대한 해결책으로 표현을 하나로 합치고, 버그를 방지하기 위해서!

dependency array를 주어 특정 state의 변경에만 반응하게 할 수 있다.
이전에는 componentDidUpdate(prevProps, prevState) { ... } 두 가지 값을 받으면서 두 가지의 값이 바뀌었을 때 현재 값과 이전 값을 비교해야 했다.

정리

clean up: 구독 해지나, 타이머의 클리어

Custom Hooks


컴포넌트들의 중복되는 Hook 로직을 묶어서 재사용 하도록 한다.
Hook에서 Hook으로 정보 전달이 가능하다.

사용 규칙

  • 커스텀 훅의 이름은 'use'로 시작해야 한다.
  • 너무 이른 단계에서 로직을 뽑아내려고 하지 않아도 된다. 쓰면서 반복된다고 생각되면 그때 뽑아내도 된다.

🤔 Hook 정리

  • useState: 이전 값을 인자로, 초기화 지연(함수)
  • useEffect: 의존성 배열(안 주거나, [], [a, b])
  • useLayoutEffect: useEffect와 유사. 모든 DOM 변경 후 브라우저가 화면을 그리기 이전 시점에 동기적으로 수행된다.
  • useReducer: useState를 대체할 수 있다. (state/reducer/action)
  • useRef: current라는 상자에 어떤 값을 넣어주는 것이다. 하지만 내용의 변경은 알려주지 않으므로 콜백 ref를 사용한다.

정리


2. Composition


공식문서: https://ko.reactjs.org/docs/composition-vs-inheritance.html#gatsby-focus-wrapper

합성 (Composition) vs 상속 (Inheritance)


예제 1

컴포넌트에 다른 컴포넌트를 담을 수 있다. children으로 할 수도, custom으로 할 수도 있다. (해당 예제에서는 custom. titledescription)

// `Dialog.jsx`

import React from 'react'
// 모든 Dialog에 일괄적으로 동작해야 하는 것들이 포함됨

export default function Dialog(props) {
  return (
    <div style={{backgroundColor: 'pink'}}>
      {props.children}
    </div>
  )
}
// CustomDialog.jsx

import React from 'react'
import Dialog from './Dialog'
// title이나 description을 써야 할 때

export default function CustomDialog(props) {
  return (
    <Dialog>
      <h1>{props.title}</h1>
      <h5>{props.description}</h5>
    </Dialog>
  )
}
// WelcomeDialog.jsx

import React from 'react'
import CustomDialog from './CustomDialog'
import Dialog from './Dialog'
// 전혀 새로운 것들

export default function WelcomeDialog() {
  // return (
  //   <Dialog>
  //     <h1>Welcome</h1>
  //     <h5>Thank you!</h5>
  //   </Dialog>
  // )
  return (
    <CustomDialog title="Welcome" description="Thanks" />
  )
}

예제 2

예제 1을 더 확장.
전달받은 props의 타입이 string이라면 이미 커스텀해둔 것을 사용, string이 아니라면 그것을 보여주었다.

// Dialog.jsx

import React, {useState} from 'react'

export default function Dialog(props) {
  const [isOpen, setisOpen] = useState(false);

  return (
    <>
      <button onClick={() => setisOpen(true)}>Open</button>
      {isOpen && (
        <div
          style={{
            position: "absolute",
            zIndex: 99,
            top: "50%",
            left: "50%",
            transform: "translate(-50%, -50%)",
            border: "1px solid black",
            padding: 24,
            backgroundColor: "white"
          }}
          >
            {typeof props.title === "string" ? (
              <h1>{props.title}</h1>
            ) : (
              props.title
            )}
            {typeof props.description === "string" ? (
              <h5>{props.description}</h5>
            ) : (
              props.description
            )}
            {typeof props.button === "string" ? (
              <button
                style={{backgroundColor:"red", color:"white"}}
                onClick={() => setisOpen(false)}
              >
                {props.button}
              </button>
              ) : (
                <div onClick={() => setisOpen(false)}>{props.button}</div>
            )}
            
          </div>
        )}
        {isOpen && (
          <div 
            style={{
              position: "fixed",
              top: 0,
              left: 0,
              bottom: 0,
              right: 0,
              backgroundColor: "lightgray"
            }}
          />
      )}
    </>
  )
}
// ThankyouDialog.jsx

import React from 'react'
import Dialog from './Dialog'

export default function ThankyouDialog() {
  return (
    <Dialog
      title={<h1 style={{color: 'purple'}}>Thanks</h1>}
      description="It is honor to meet you!"
      button={
        <button style={{backgroundColor: "blue", color:"white"}}>
          close
        </button>
      }
    />
  )
}

정리


3. HOC


공식문서: https://ko.reactjs.org/docs/higher-order-components.html#gatsby-focus-wrapper

고차 컴포넌트(High Order Component)는 컴포넌트 로직을 재사용하기 위한 리액트의 고급 기술이다. 리액트 API의 일부가 아니며, 리액트의 구성적 특성에서 나오는 패턴이다. (리액트에만 존재하는 것이 아닌, 다른 곳에서도 사용될 수 있다는 의미!)

고차 컴포넌트는 컴포넌트를 가져와 새 컴포넌트를 반환하는 함수이다. 더하기를 수행하는 함수의 경우 1, 2를 인자로 받으면 3을 반환한다. 마찬가지로 고차 컴포넌트도 함수지만, 인자로 컴포넌트를 받고 새로운 컴포넌트를 반환하는 함수인 것이다. (A라는 컴포넌트를 인자로 주면 A컴포넌트에 어떤 기능을 추가하여 새 컴포넌트로 반환하는..)

const EnhancedComponent = higherOrderComponent(WrappedComponent);

사용 규칙

  • 원본 컴포넌트를 변경하면 안 된다. (오버라이드 등..)
  • render 메소드 안에서 사용하면 안 된다.
    • 리액트는 렌더링 최적화를 할 때 항상 변경점을 보는데 HOC를 사용하면 계속해서 새로운 컴포넌트가 생성된다. 이렇게 되면 변경사항이 생기므로 전체 서브트리를 다시 그리게 되고, 이는 성능 저하 문제를 야기한다.
  • ref는 전달되지 않는다.
    • 실제 prop이 아닌 key처럼 특별하게 취급된다.

예제

실행 시 'Loading...' 문구가 나타나고, 1초 후에는 button과 input이 나타난다.
이 기능이 button과 input에 모두 해당되므로 재사용 할 수 있도록 HOC를 만들었다.

withLoading()은 인자로 컴포넌트를 받고, 컴포넌트를 반환한다.
WithLoadingComponent()는 함수형 컴포넌트이다.

// withLoading.jsx

import React, {useState, useEffect} from 'react'

export default function withLoading(Component) {
  const WithLoadingComponent = (props) => {
    const [loading, setLoading] = useState(true);
  
    useEffect(() => {
      const timer = setTimeout(() => setLoading(false), 1000);
  
      return () => clearTimeout(timer);
    }, [])
  
    return loading ? <p>Loading...</p> : <Component {...props}/>
  };

  return WithLoadingComponent;
};
// Input.jsx

import React from 'react'
import withLoading from './withLoading'

 function Input() {
  return (
    <input defaultValue="Input" />
  )
}

export default withLoading(Input);
// Button.jsx

import React from 'react'
import withLoading from './withLoading'

function Button() {
  return <button>Button</button>;
}

export default withLoading(Button);

👉 결과

정리


profile
🙋‍♀️

0개의 댓글