Custom Hooks

e-pong:)·2022년 11월 25일
0

Custom Hooks란?

개발자가 스스로 커스텀한 훅을 의미하며, 반복되는 로직을 함수로 뽑아내어 재사용 할 수 있습니다.
여러 urlfetch할 때, 여러 Input에 의해 상태 변경 등 반복되는 로직을 동일한 함수에서 작동하게 하고 싶을 때 커스텀 훅을 주로 사용한다.

Custom Hooks을 사용하게 될 경우

  1. 상태관리 로직의 재활용이 가능하고
  2. 클래스 컴포넌트보다 적은 양의 코드로 로직을 구현할 수 있으며
  3. 함수형으로 작성하기 때문에 보다 명료하다는 장점이 있다.
//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 컴포넌트는 사용자들의 상태에 따라 온라인이라면 초록색으로 표시하는 컴포넌트이다.
이 두 컴포넌트는 정확하게 똑같이 쓰이는 로직이 존재하고 있습니다.

//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 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 규칙

  1. Custom Hook을 정의할 때는 함수 이름 앞에 use를 붙여야 한다.
  2. 대개의 경우 프로젝트 내의 hooks 디렉토리에 Custorm Hook을 위치 시킨다.
  3. Custom Hook으로 만들 때 함수는 조건부 함수가 아니어야한다. 즉, return 하는 값은 조건부여서는 안된다.

이렇게 만들어진 Custom Hook은 hook 내부에 useState 와 같은 React 내장 hook을 사용하여 작성할 수 있다. 일반 함수 내부에서는 hook을 불러 사용할 수 없지만 Custom 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>
  );
}

만들어 놓은 useFriendStatus Hook을 두 컴포넌트에 적용한 모습이다.
두 컴포넌트가 더 직관적으로 보이는 것을 확인 할 수 있다.

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

Custom Hook 예시

  1. 여러 url을 fetch할 때 쓸 수 있는 useFetch 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;
  1. 여러 input에 의한 상태 변경을 할 때 쓸 수 있는 useInputs Hooks
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;
profile
말에 힘이 있는 사람이 되기 위해 하루하루, 성장합니다.

0개의 댓글