React Custom Hook 1부

jimmy neutron·2022년 9월 10일
0

React

목록 보기
1/3

들어가기 전에..

예전에 토이 프로젝트를 하면서 코드가 복잡해지거나 side effect가 많은(useEffect가 여러번 사용된다거나) 컴포넌트는 별 생각 없이 커스텀훅을 만들어서 분리를 했었다.

그때는 커스텀훅을 막연하게 코드의 가독성을 높이고 상태의 의존성을 줄이기 위해 쓰는 훅으로만 알았는데 문득 내가 쓰고 있는 방식이 옳은가에 대한 의문이 생겼다. 그래서 이번 기회에 확실하게 파헤쳐보려고한다.

React custom hook 이란??

코드의 중복을 줄이고 재사용성을 높이기 위해 개발자가 스스로 만든 훅을 말한다.

결론부터 말하자면 중복을 줄이고 재사용성을 높이는것이 가장 큰 장점이다.

예를 들어보자.

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';
}

서버에서 유저 정보를 받아와서 online/offline상태를 알려주는 컴포넌트이다.

이제 연락처 목록에서 온라인 상태인 사용자들의 이름을 초록색으로 표시하는 코드를 짠다고 가정해보자.

아마 위의 로직과 거의 흡사한 FriendListItem이라는 컴포넌트를 만들면 될것이다.

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

리턴값 외에는 모든 로직이 동일하다. 이 경우에 동일한 코드를 커스텀 훅으로 만들고 로직이 필요한 컴포넌트에서 불러와 사용하면 되는것이다.

커스텀 훅 생성

import { useState, useEffect } from 'react';

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;
}

여기서 주의할 점은 커스텀훅은 use로 시작하는 자바스크립트 함수라는 점이다. 여기서 한가지 의문이 들 수 있는데 리액트에 내장된 hook들은 jsx파일 즉, 컴포넌트 내에서만 사용할 수 있다는 것이다.
다시 말해 자바스크립트 함수에서는 hook을 사용할 수 없는데 유일하게 커스텀훅만이 다른 hook을 사용할 수 있다.

커스텀 훅 적용

이제 위의 두 컴포넌트를 커스텀훅을 사용하게 바꿔주면 되는데

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를 공유하지 않는다.
커스텀훅을 사용할 때마다 그 안의 state와 effect는 완전히 독립적이다.

커스텀 훅으로 분리하는 기준

그렇다면 실제 코드에서 무엇을 기준으로 커스텀 훅을 만들어야할까?
내가 지금까지 했던 실수는 함수 컴포넌트의 길이가 길어지면 무조건 분리하려고 했던 점이다. 단지 코드가 길어졌고 가독성이 떨어졌다고 해서 분리할 필요는 없다. 커스텀 훅의 분리 기준은 세 가지 정도로 추려볼 수 있는데

  1. 복잡한 로직을 단순한 인터페이스 속에 숨길 수 있는 경우
  2. 복잡하게 뒤엉킨 컴포넌트를 풀어서 컴포넌트간 의존성을 낯출 수 있는 경우
  3. 재사용 가능하고 반복되는 로직이 있는 경우

이다.
여기서 1,2번에 대한 내용은 useReducer에 관한 내용인데 다음에 자세하게 풀어보도록 하겠다.

참고
리액트 공식 사이트
함수형 프로그래밍

profile
프론트엔드로 지구정복

0개의 댓글