Custom Hooks

신창용·2023년 1월 25일
0

React 심화

목록 보기
6/7

Custom Hooks란 개발자가 스스로 커스텀한 훅을 의미하며 이를 이용해 반복되는 로직을 함수로 뽑아내어 재사용할 수 있다.

여러 url을 fetch할 때, 여러 input에 의한 상태 변경 등 반복되는 로직을 동일한 함수에서 작동하게 하고 싶을 때 커스텀 혹을 주로 사용한다. 이를 이용하면

  1. 상태관리 로직의 재활용이 가능하고
  2. 클래스 컴포넌트보다 적은 양의 코드로 동일한 로직을 구현할 수 있으며
  3. 함수형으로 작성하기 때문에 보다 명료하다는 장점이 있다.

예를 들어 이런 컴포넌트가 있다고 보자. 해당 컴포넌트는 실제 React 공식 문서에 있는 컴포넌트이다.

//FriendStatus : 친구가 online인지 offline인지 return하는 컴포넌트
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';
}

//FriendListItem : 친구가 online일 때 초록색으로 표시하는 컴포넌트
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>
  );
}

FriendStatus컴포넌트는 사용자들이 온라인인지 오프라인인지 확인하고, FriendListItem 컴포넌트는 사용자들의 상태에 따라 온라인이라면 초록색으로 표시하는 컴포넌트이다.
이 두 컴포넌트는 정확하게 똑같이 쓰이는 로직이 존재하고 있다.
이 로직을 빼내서 두 컴포넌트에서 공유할 수는 없을까?
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;

두 컴포넌트에서 사용하기 위해 동일하게 사용되고 있는 로직을 분리하여 함수 useFriendStatus로 만든다.
이렇게 Custom Hook을 정의할 때는 일종의 규칙이 필요하다.

  • Custom Hook을 정의할 때는 함수 이름 앞에 use를 붙이는 것이 규칙이다.
  • 대개의 경우 프로젝트 내의 hooks 디렉토리에 Custom Hook을 위치 시킨다.
  • Custom Hook으로 만들 때 함수는 조건부 함수가 아니어야 한다.
    즉 return 하는 값은 조건부여서는 안 된다.
    그렇기 때문에 위의 이 useFriendStatus Hook은 온라인 상태의 여부를 boolean탑입으로 반환하고 있다.

이렇게 만들어진 Custom Hook은 내부에 useState와 같은 React 내장 Hook을 사용하여 작성할 수 있다.
일반 함수 내부에서는 React 내장 Hook을 불러 사용할 수 없지만 Custom Hook 에서는 가능하다는 것 또한 알아두면 좋다.

이제 이 useFriendStatus 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>
  );
}

로직을 분리해 Custom Hook으로 만들었기 때문에 두 컴포넌트는 더 직관적으로 확인이 가능해진다.

그러나 같은 Custom Hook을 사용했다고 해서 두 개의 컴포넌트가 같은 state를 공유하는 것은 아니다. 그저 로직만 공유할 뿐, state는 컴포넌트 내에서 독립적으로 정의 되어 있다.


Custom Hook의 예시

const useFetch = ( initialUrl:string ) => {
	const [url, setUrl] = useState(initialUrl);
	const [value, setValue] = useState('');

	const fetchData = () => axios.get(url).then(({data}) => setValue(data));	

	useEffect(() => {
		fetchData();
	},[url]);

	return [value];
};

export default useFetch;

[코드] 여러 url을 fetch할 때 쓸 수 있는 useFetch Hook

import { useState, useCallback } from 'react';

function useInputs(initialForm) {
  const [form, setForm] = useState(initialForm);
  // change
  const onChange = useCallback(e => {
    const { name, value } = e.target;
    setForm(form => ({ ...form, [name]: value }));
  }, []);
  const reset = useCallback(() => setForm(initialForm), [initialForm]);
  return [form, onChange, reset];
}

export default useInputs;

[코드] 여러 input에 의한 상태 변경을 할 때 쓸 수 있는 useInputs Hooks

profile
코딩으로 쓰는 일기장

0개의 댓글