[React] Hook의 규칙과 Custom Hook - 커스텀 훅 특징 및 사용법

    Hook의 규칙

    1. Hook은 무조건 최상위 레벨에서만 호출해야 한다.

    • 반복문이나 조건문, 중첩된 함수들 안에서 Hook을 호출할 수 없다.
    • Hook은 컴포넌트가 렌더링 될 때마다 매번 같은 순서로 호출되어야 한다.
      ⏩ useState(), useEffect()를 호출해서 컴포넌트의 state를 올바르게 관리할 수 있도록 한다.

     

    📌 예제1. 잘못된 Hook 사용법

    import { useEffect } from 'react';
    
    function MyComponent(props) {
      const [name, setName] = useState('yuns');
    
      if(name !== '') {
        useEffect(()=> {
          ...
        });
      }
      ...
    }

    ⏩ 렌더링할 때마다 Hook이 같은 순서로 호출되지 않고, 조건문의 결과에 따라 호출되는  Hook이 달라진다.

     

    2. 리액트 함수 컴포넌트에서만 Hook을 호출해야 한다. ( Class Component ❌ )

    • 일반적인 자바스크립트 함수에서 Hook을 호출하면 안 된다.  ✅ 리액트 함수 컴포넌트, 혹은 커스텀 훅에서만 호출 가능

     

    eslint-plugin-react-hooks

     

     

    Custom Hook 만들기

    • 목적 : 여러 컴포넌트에서 반복되는 로직을 Hook으로 만들어 재사용하기 위해

    Custom Hook을 만들어야 하는 상황

    📌 예제2-1. 리소스 1

    import React, { useState, useEffect } from "react";
    
    function UserStatus(props) {
      const [ isOnline, setIsOnline ] = useState(null);
    
      useEffect(()=>{
        function handleStatusChange(status) {
          setIsOnline(status.isOnline)
        }
    
        ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
        return () => {
          ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
        };
      });
    
        if (isOnline === null) {
          return '대기중...'
        }
        return isOnline ? '온라인' : '오프라인';
    }

    📌 예제2-2. 리소스 2

    import React, { useState, useEffect } from "react";
    
    function UserStatus(props) {
      const [ isOnline, setIsOnline ] = useState(null);
    
      useEffect(()=>{
        function handleStatusChange(status) {
          setIsOnline(status.isOnline)
        }
    
        ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
        return () => {
          ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
        };
      });
    
        return (
          <li style={{ color : isOnline ? 'green' : 'black'}}>
            {props.user.name}
          </li>
    )
    }

    ✅ 두 코드 중 중복되는 부분을 Custom Hook으로 추출한다.

     

    Custom Hook 추출하기

    중복되는 코드 존재하면, Custom Hook으로 만들어준다.

    • Custom Hook : 이름이 'use-'로 시작하고, 내부에서 다른 Hook을 호출하는 하나의 자바스크립트 함수

    📌 예제2-3. useUserStatus 커스텀 훅

    import { useState, useEffect } from "react";
    
    function useUserStatus(userId) {
      const [ isOnline, setIsOnline ] = useState(null);
    
      useEffect(()=>{
        function handleStatusChange(status) {
          setIsOnline(status.isOnline)
        }
    
        ServerAPI.subscribeUserStatus(props.user.id, handleStatusChange);
        return () => {
          ServerAPI.unsubscribeUserStatus(props.user.id, handleStatusChange);
        };
      });
    
      return isOnline;
    }
    • 두 개의 컴포넌트에서 중복된 로직을 추출해서 가져옴
    • 다른 컴포넌트(함수) 내부에서와 마찬가지로,
      다른 Hook을 호출하는 것은 무조건 커스텀 훅의 최상위 레벨에서만 해야 한다.
    • 커스텀 훅은, 다른 훅과 달리 단순한 함수에 지나지 않는다. ( 파라미터, 리턴 값을 사용자가 지정할 수 있다. ) 
    • ➰ 다만, 앞에 'use-'를 붙여줘서 이것이 훅이라는 것을 명시해 준다.

     

    Custom Hook 사용하기

    • Custom Hook의 이름은 꼭 'use-'로 시작해야 한다.
    • 여러 개의 컴포넌트에서 하나의 Custom Hook을 사용할 때, 컴포넌트 내부에 있는 모든 state와 effects는 전부 분리되어 있다. ⏩ state, effect에 대한 로직을 공유할 뿐, 값을 공유하지는 않는다.
      ✅ 각 Custom Hook 호출에 대해서 분리된 state를 얻게 된다.
    • 각 Custom Hook의 호출 또한 완전히 독립적이다.

     

    Hook들 사이에서 데이터를 공유하는 방법

     

    📌 예제3.

    const userList = [
      { id: 1, name: 'Yuns'}
      { id: 2, name: 'Mike'}
      { id: 3, name: 'Steve'}
    ];
    
    function ChatUserSelector(props) {
      const [userId, setUserId] = useState(1);
      const isUserOnline = useUserStatus(userId);
    
      return (
        <>
          <Circle color={isUserOnline ? 'green' : 'red'} />
          <select
            value={userId}
            onChange={event=> setUserId(Number(event.target.value))}
          >
            {userList.map(user=>(
              <option key={user.id} value={user.id}>
                {user.name}
              </option>
            ))}
          </select>
        </>
      )
    }

    useState()를 통해 데이터를 선언하고, 커스텀 훅 userUserStatus()가 이를 매개변수로 받고 있다.

    댓글