[Worksheet 220516] React 공식문서 고급 - Hooks

방예서·2022년 5월 16일
0

Worksheet

목록 보기
34/47
React 공식문서로 디테일 잡기(고급)

Hooks

class의 단점을 보완하면서 라이프 사이클 등과 관련된 함수를 재사용 가능하게 한다.
Hook을 이용하여 기존의 class 바탕의 코드를 작성할 필요없이 상태 값과 여러 react 기능을 사용할 수 있다.

Hooks의 등장 이유

  • 컴포넌트 사이에서 상태 로직 재사용의 어려움
    Hook은 계층의 변화 없이 상태 관련 로직을 재사용할 수 있도록 도와준다.

  • 복잡한 컴포넌트 이해의 어려움
    복잡한 상태 관련 로직들과 사이드 이펙트가 있는 컴포넌트들을 사용할 때 관련 없는 로직이 섞이곤 한다. DidMount, DidUpdate, WillUnMount 등등 ... 특히 얘네는 한 공간 안에 묶여있어서 작게 분리하는 것이 불가능하다.
    Hook을 이용해 서로 비슷한 것을 하는 작은 함수의 묶음으로 컴포넌트를 나눌 수 있다.

  • Class 사용의 어려움
    state를 사용할 필요가 없어서 함수 컴포넌트로 사용하다가, 나중에 state를 사용해야한다는 것을 알면 구조를 재조정해야하는 일이 있을 수 있다.
    Hook을 통해 Class 없이 React 기능들을 사용할 수 있게 되었다.

Hook 사용 규칙

Hook은 자바스크립트 함수이지만, 두 가지 규칙을 준수해야한다.

  • 최상위에서만 Hook을 호출해야한다.
    반복문, 조건문, 중첩된 함수 내에서 Hook을 실행하지 않도록 한다.

  • React 함수 컴포넌트 내에서만 Hook을 호출해야한다. (+ custom Hook)
    일반 JS 함수에서는 Hook을 호출하지 않도록 한다.

useState

함수 컴포넌트에서는 React state를 사용할 수 없는데, useState 함수를 사용하면 함수 컴포넌트 안에서 state를 사용할 수 있다.

// class component
class Example extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      count: 0
    };
  }
// functional component with hook
import React, { useState } from 'react';

function Example() {
// 새로운 state 변수를 선언하고, 이것을 count라 부르겠습니다.
const [count, setCount] = useState(0);
  • useState가 하는 일
    state 변수를 선언할 수 있다.
    클래스 컴포넌트의 this.state 가 제공하는 기능과 같다.

  • useState의 인자
    state의 초기값을 넘겨준다. 클래스와 달리 객체일 필요는 없고, 숫자/문자 타입을 가질 수 있다.

  • useState의 반환값
    state 변수, 해당 변수를 갱신할 수 있는 함수
    이 두 가지 쌍을 반환한다.

useEffect

effect hook을 사용하면 함수 컴포넌트에서 side effect를 수행할 수 있다.

데이터 가져오기, 구독(subscription) 설정하기, 수동으로 React 컴포넌트의 DOM을 수정하는 것까지 이 모든 것이 side effects 이다.

클래스 컴포넌트에서의 componentDidMount와 componentDidUpdate, componentWillUnmount가 합쳐진 것과 같다.

clean-up을 이용하지 않는 effects

React가 DOM을 업데이트한 뒤 추가로 코드를 실행해야 하는 경우가 있다. 네트워크 리퀘스트, DOM 수동 조작, 로깅 등은 정리(clean-up)가 필요 없는 경우들이다. 이러한 예들은 실행 이후 신경 쓸 것이 없다.

  • useEffect가 하는 일
    react에게 컴포넌트가 렌더링 이후에 어떤 일을 수행해야하는지 말한다.
    우리가 넘긴 함수를 기억했다가 DOM이 업데이트를 수행한 이후에 불러낸다.

  • useEffect를 컴포넌트 내에서 부르는 이유
    컴포넌트 내부에 두면 effect를 통해 state 변수에 접근할 수 있게 된다.

clean-up을 이용하는 effects

외부 데이터에 구독을 설정해야하는 경우를 생각하자. 이런 경우에서 메모리 누수가 발생하지 않도록 정리를 꼭 해주어야 한다.

클래스 컴포넌트에서는 구독과 구독해지를 한 공간에서 하지 못한다.

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    // effect 이후에 어떻게 정리(clean-up)할 것인지 표시합니다.
    return function cleanup() {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  • effect에서 함수를 반환하는 이유
    effect를 위한 추가적인 정리 메커니즘이다.

  • react가 effect를 정리하는 시점
    컴포넌트가 마운트 해제되는 때에 정리를 실행한다.

정리 함수는 반드시 유명함수를 반환해야하는 것은 아니다. 화살표 함수를 쓰거나 다른 이름으로 불러도 된다.

useEffect의 dependency array

useEffect의 선택적 인수인 두번째 인수로 배열을 넘기면 특정 값에 대한 랜더링을 제어할 수 있다.

  • 아무 값을 주지 않을 경우
    항상 렌더링 된다.

  • 빈 배열을 줄 경우
    맨 처음에 한 번만 렌더링 된다.

  • 어떤 값이 들어있는 배열
    그 값이 변경 될 때만 렌더링 된다.

Custom Hook

자신만의 Hook을 만들어 컴포넌트 로직을 함수로 뽑아내어 재사용할 수 있다.
custom hook을 만들 때는 이름을 use 로 시작하게 한다. 관습이지만 아주 중요하다.

예시로 채팅 애플리케이션에서 친구가 온라인 상태인지에 대한 메시지를 표시하는 컴포넌트를 보자.

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

function FriendStatus(props) {
  // 주목
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  // 주목
  
  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

다음으로는 온라인 상태의 사용자들의 이름을 초록색으로 표시하는 상황을 보자.

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

function FriendListItem(props) {
  // 주목
  const [isOnline, setIsOnline] = useState(null);
  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }
    ChatAPI.subscribeToFriendStatus(props.friend.id, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(props.friend.id, handleStatusChange);
    };
  });
  // 주목

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

주석으로 감싸놓은 부분이 중복되고 있다.
중복되는 부분을 사용자 custom hook으로 만들어 재사용할 수 있다.

import { useState, useEffect } from 'react';

// 중복되는 부분의 custom hook
function useFriendStatus(friendID) {
  const [isOnline, setIsOnline] = useState(null);

  useEffect(() => {
    function handleStatusChange(status) {
      setIsOnline(status.isOnline);
    }

    ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
    return () => {
      ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
    };
  });

  return isOnline;
}

function FriendStatus(props) {
  const isOnline = useFriendStatus(props.friend.id);

  if (isOnline === null) {
    return 'Loading...';
  }
  return isOnline ? 'Online' : 'Offline';
}

function FriendListItem(props) {
  const isOnline = useFriendStatus(props.friend.id);

  return (
    <li style={{ color: isOnline ? 'green' : 'black' }}>
      {props.friend.name}
    </li>
  );
}

같은 Hook을 사용하는 두 컴포넌트 내의 state와 effect는 완전히 독립적이다.
각각의 Hook에 대한 호출은 서로 독립된 state를 받는다.

추가 Hooks

  • useContext

  • useReducer
    useState의 대체 함수.
    (state, action) => newState의 형태로 reducer를 받고 dispatch 메서드와 짝의 형태로 현재 state를 반환한다.

  • useCallback

  • useMemo

  • useRef

  • useImperativeHandle

  • useLayoutEffect

  • useDebugValue

profile
console.log('bang log');

0개의 댓글