Hook(useState, useEffect 등)에는 두 가지 규칙이 있습니다:
- hook은 최상위 레벨에서만 호출하세요 — 조건문, 반복문, 중첩 함수 내부에서는 절대 호출하지 마세요.
- hook은 React 함수에서만 호출하세요 — 컴포넌트 또는 다른 custom hook에서만 호출합니다.
jsx
() {
[user, setUser] = ();
(id) {
[x] = ();
}
}
React는 hook을 이름으로 식별하지 않습니다 — 호출 순서에 의존합니다. 매 render마다 정확히 동일한 순서로 hook을 따라가며 각 호출을 내부 리스트의 슬롯에 매칭합니다:
Render 1: useState(A) → slot 0, useState(B) → slot 1, useEffect → slot 2
Render 2: useState(A) → slot 0, useState(B) → slot 1, useEffect → slot 2
hook을 조건부로 호출하면 render 사이에서 순서가 바뀔 수 있습니다. 그러면 slot 1이 한 render에서는 useState(B)이고 다음 render에서는 useEffect일 수 있으며 — React는 잘못된 state를 잘못된 hook에 전달하여 모든 것을 손상시킵니다.
hook은 무조건 호출하고, 조건을 그 내부에 두거나 반환된 값에 따라 분기하세요:
const [user, setUser] = useState(null);
useEffect(() => {
if (!id) return; // 조건을 effect를 감싸는 게 아니라 effect 내부에 둠
fetchUser(id).then(setUser);
}, [id]);
eslint-plugin-react-hooks 린트 규칙이 이러한 규칙을 자동으로 강제합니다.