React.js: Custom Hooks

Beautify.log·2021년 11월 5일
0
post-thumbnail

안녕하세요. 이번 포스팅에서는 React.js의 커스텀 Hooks에 대해 살펴보고자 합니다. 공식문서에서는 자신만의 Hook, 사용자 정의 Hook이라고 정의하고 있습니다.

여러개의 컴포넌트에서 비슷한 기능을 공유하는 경우에는 자기만의 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인지 Off라인인지 반환해주는 컴포넌트입니다.

채팅 앱에 연락처 목록이 있다고 가정할 때, 해당 목록에 있는 사람들의 On/OffLine을 초록색과 검은색으로 표현해주는 컴포넌트로 바꿔보고자 한다고 합시다.

로직을 복사해서 리스트 컴포넌트에 붙여넣기로 적용시킬 수 있겠지만 '중복'은 유지보수하는데 힘이 들게 합니다...

그렇다면 우선 JSX를 return 해주는 부분만 살짝 고쳐보겠습니다.

...

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

이제 바뀐 로직을 다른 곳에서 공유하고자 합니다.

가급적이면 트리에 컴포넌트를 추가하지 않고 로직을 분리하여 해결하는 것이 추후에 유지보수 등의 측면에서 효율적이라고 생각합니다.

로직 분리하기

새로운 로직을 작성해봅시다.

// useFriendStatus.js

import { useState, useEffect } from 'react';

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

함수의 이름이 일단 바뀌었습니다. 커스텀 Hooks는 use로 시작하는 이름으로 명명해 주는 것이 원칙입니다. 이는 한눈에 봤을 때 Hooks의 규칙이 적용되는지 판단하기 위함입니다. 이 규칙을 따르지 않으면 특정한 함수가 그 안에서 Hook을 호출하는지를 알 수 없기 때문에 Hook 규칙의 위반 여부를 자동으로 체크할 수 없습니다.

본문은 위의 로직에서 복사해 온 것이며, 커스텀 Hooks를 만들 때 조건부 함수가 아니어야 한다는 사실에 유의해주세요.

분리한 Hook 사용하기

자, 위에서 useFriendStatus 로직을 만들어주었으니 이제 다른 곳에서 사용해 보아야겠죠?

// FriendStatus.js

import React from 'react';
import { useFriendStatus } from './useFriendStatus';

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

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

함수 내부에서 좀전에 뽑아낸 로직을 가져와 사용하고 있습니다.

친구 리스트의 On/Off Line 여부를 색깔로 표기하는 컴포넌트도 마찬가지로,

import React from 'react';
import { useFriendStatus } from './useFriendStatus';

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

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

와 같이 사용할 수 있게 됨을 알 수 있습니다.

참고사항

두 개 이상의 컴포넌트에서 하나의 Custom Hooks를 공유하여 사용할 때, state를 공유하는지에 대해 궁금할 수도 있습니다.
Custom Hooks는 상태관련 로직을 재사용하는 machanism이지만 이 Hook을 사용할 때마다 그 안의 state와 effect는 완전히 독립적이라는 사실을 인지해야 합니다.

정리하기

React.js에서 Hook을 사용하면 클래스형 컴포넌트(복잡함...)를 작성하지 않아도 보다 많은 기능을 구현할 수 있습니다. 그렇지만 클래스형 컴포넌트를 사용하는 방식이 잘못되었다고 보기는 어렵습니다.

기존의 클래스형 컴포넌트도 React에서는 계속 지원된다고 합니다. 만약에 지금 설계되어 유지보수하고 있는 프로젝트에서 클래스형 컴포넌트를 사용하고 있다하더라도 굳이 함수형 컴포넌트로 고치지 않아도 되겠습니다.

다만 새롭게 만들어가는 프로젝트에서는 가급적이면 함수형 컴포넌트와 Hooks를 기반으로 진행할 것을 권고하고 있습니다. 그렇기 때문에 함수형 컴포넌트의 사용을 우선적으로 해주시고 필요한 경우에 한하여 클래스형 컴포넌트를 만들어주시면 되겠습니다.

이상으로 React.js Hooks 튜토리얼을 마칩니다!

profile
tried ? drinkCoffee : keepGoing;

0개의 댓글