문제 상황
스크린샷처럼 react hooks이 적용된 컴포넌트에서 ‘Warning: React has detected a change in the order of Hook ~’에러가 발생하는 것을 볼 수 있습니다.
문제 확인
에러 문구 그대로 hooks의 순서가 변경됨에 따른 경고입니다. 위 사진에서는 useContext -> undefined 순서로 호출되던 hooks가 useContext -> useState 순서로 바뀐 것을 볼 수 있습니다.
hooks의 순서가 변경되면 안되는 이유?
리액트에서 rooks의 주요 제약은 크게 아래 2가지 입니다.
- hooks는 함수형 컴포넌트 내부, 최상단에서 호출되어야 한다(조건문, 반복문, 함수 내부 X).
- hooks는 컴포넌트의 re-render마다 동일한 순서로 호출되어야 한다.
https://react.dev/warnings/invalid-hook-call-warning
그렇다면 hooks에 위와 같은 제약을 추가한 이유는 무엇일까요? 가장 큰 것은 의존성 관리 문제라고 생각합니다.
Hooks는 의존성 배열에서 지정된 순서대로 호출되어야 합니다. 예를 들어, useState나 useEffect의 의존성 배열에 지정된 변수들은 해당 변수가 변경될 때마다 적절하게 동작해야 합니다. 호출 순서가 바뀌면 의존성 관리가 제대로 되지 않아 원하지 않는 동작이 발생할 수 있습니다.
추가로 호출 순서가 일정하지 않다면 코드의 디버깅의 난이도가 급격하게 상승하며 코드의 가시성 역시 떨어지게 됩니다.
문제 해결
문제 확인 부분에서 살핀 것처럼 hooks의 규칙에 따라 함수형 컴포넌트 내부, 최상단에 호출하면 됩니다(조건부로 할당하는 부분이 있다면 조건 밖으로 빼줍니다).
function ExampleComponent({ show }) {
const [count, setCount] = useState(0);
// 조건부로 useEffect를 호출하는 잘못된 예제
if (show) {
useEffect(() => {
console.log('Component did mount or update');
}, []);
}
…
}
위와 같은 코드가 있는 경우에 useEffect는 show에 따라 호출 여부가 갈리게 됩니다. 즉, 동일한 순서를 보장할 수 없습니다.
function ExampleComponent({ show }) {
const [count, setCount] = useState(0);
// useEffect를 최상위에서 호출하고, 내부에서 조건부 로직을 처리
useEffect(() => {
if (show) {
console.log('Component did mount or update');
}
}, [show]);
…
}
에러를 해결하기 위해서는 위처럼 코드를 수정할 수 있습니다.
마무리
hooks 호출 순서와 관련된 이슈는 JSX의 early return 현상으로 인하여 사전에 발견하기 어렵고 많이 놓치는 부분입니다.
특히, hooks을 적용, 미적용 하는 것을 위하여 호출 여부를 조작하고 싶은 것도 자연스러운 생각이지만 hooks의 규칙을 생각하며 잘못된 접근임을 알 수 있습니다.
이처럼 hooks의 호출 순서를 보장하는 것은 중요하며 적용, 미적용을 위한 로직은 hooks의 내부에서 작성한다고 생각하면 좋을 것 같습니다.
관련된 eslint도 있기에 기회가 된다면 소개하는 포스팅으로 찾아오도록 하겠습니다.
'Web > React' 카테고리의 다른 글
[React] React에서 API 호출을 효과적으로 막는 방법 - useRef 활용하기 (0) | 2024.11.22 |
---|---|
[React] 접근 제한을 위한 Private Route 만들기 (0) | 2024.04.21 |
[React] Invalid Hook Call Warning 해결하기 (0) | 2024.04.15 |
[Recoil] React를 위한 상태 관리 라이브러리, Recoil에 대하여 알아보자 (2) | 2024.02.07 |
[React + Vite] "Failed to load resource: the server responded with a status of 404(not found)" 해결하기 (0) | 2023.11.16 |