본문 바로가기
Front/React

[React] Hook 의 규칙과 Custom Hook 만들기

by 오엥?은 2023. 4. 18.
반응형

 

✔ Hook 의 규칙

 

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

  ->  반복문이나 조건문 또는 중첩된 함수들 안에서 Hook 을 호출하면 안 된다.

② Hook 은 컴포넌트가 렌더링될 때마다 매번 같은 순서로 호출되어야 한다.

  ->  이렇게 해야 react 가 다수의 useState() Hook 과 useEffect() Hook 에 호출해서 컴포넌트의 state 를 올바르게 관리할      수 있게 된다.

 

❌ 잘못된 Hook 사용법

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

위 코드는 if 문의 조건이 true 여야 useEffect 를 호출하도록 되어있다. 이런 경우 name 의 값이 빈 문자열이 되면 조건문의 값이 false 가 되어 useEffect() Hook 이 호출되지 않는다. 결과적으로 렌더링할 때 마다 Hook 이 같은 순서대로 호출되는 것이 아니라 조건문의 결과에 따라 호출되는 Hook 이 달라지므로 잘못된 코드이다.

 

③ 리액트 함수 컴포넌트에서만 Hook 을 호출해야 한다.

  : 일반적인 자바스크립트 함수에서 Hook 을 호출하면 안 된다. 리액트 컴포넌트의 state 와 관련된 모든 로직은 소스코드를 통해 명확하게 확인이 가능해야 한다.

 

 

 

Custom Hook 만들기

  • Custom Hook 을 만들어야 하는 상황
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 ? '온라인' : '오프라인';
}

중복되는 로직을 Custom Hook 으로 추출해 보도록 하겠다.

 

Custom Hook 

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

import React, { useState, useEffect } from "react";

function useUserStatus(userId) {
    const [isOnline, setIsOnline] = useState(null);
    
    useEffect(() => {
    	function handleStatusChange(status) {
            setIsOnline(status.isOnline);
        }
        
        ServerAPI.subscribeUserStatus(userId, handleStatusChange);
        return () => {
            ServerAPI.unsubscribeUserStatus(userId, handleStatusChange);
        };
    });
    
    return isOnline;
}

위 코드는 중복되는 코드를 useUserStatus 라는 Custom Hook 으로 추출해 낸 것이다.

 

  • Custom Hook 사용하기
function UserStatus(props) {
	const isOnline = useUserStatus(props.user.id);
    
    if (isOnline === null) {
    	return '대기중...';
    }
    return isOnline ? '온라인' : '오프라인';
}

function UserListItem(props) {
    const isOnline = useUserStatus(props.user.id);
    
    return (
    	<li style={{ color: isOnline ? 'green' : 'black' }}>
            {props.user.name}
        </li>
    );
}

이 코드는 위 코드와 똑같이 작동한다. 동작의 변경이 없고, 중복된 로직만을 추출하여 Custom Hook 으로 만든 것이기 때문이다.

 

여러 개의 컴포넌트에서 하나의 Custom Hook 을 사용할 때 컴포넌트 내부에 있는 모든 state 와 effects 는 전부 분리되어 있다.

 

 

  • Hook 들 사이에서 데이터를 공유하는 방법
const userList = [
    { id: 1, name: 'Yeeun' },
    { id: 2, name: 'Heongmin' },
    { id: 3, name: 'Hyungyu' },
];

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

위 코드에서 눈여겨 봐야 할 부분은 아래와 같다.

const [userId, setUserId] = useState(1);
const isUserOnline = useUserStatus(userId);

이 코드를 보면 useState() Hook 을 사용해서 userId 라는 state 를 만들었다. 현재 선택된 사용자의 id 를 저장하기 위한 용도이다. 그리고 이 userId 는 useUserStatus 의 파라미터로 들어가게 된다. 이렇게 하면 setUserId 함수를 통해 userId 가 변경될 때마다 useUserStatus() Hook 은 이 전에 선택된 사용자를 구독 취소하고, 새로 선택된 사용자의 온라인 여부를 구독하게 된다.

 

 

 

반응형