react-router-dom의 useNavigate는 사용자의 URL를 변경하는 명령형 메서드입니다. (v6 기준)
<Link>
와 비슷한 역할을 합니다. 사용방법은 아래와 같습니다.
import { useNavigate } from "react-router-dom";
const navigate = useNavigate();
navigate('/', { replace: true });
하지만 사용하는 곳마다 매번 이렇게 사용해야하므로 모듈화를 시키고 싶었습니다.
// redirect.js
import { useNavigate } from "react-router-dom";
export const redirect = (url) => {
const navigate = useNavigate();
navigate(url, { replace: true });
};
// 사용하는 컴포넌트
import { redirect } from "../redirect";
if (사용자정보) {
redirect("/");
}
이렇게 redirect라는 함수를 만들었습니다. 원하는 곳에서 redirect(URL)만 호출하면 됩니다.
저는 서버로부터 받은 데이터에 사용자의 정보가 있으면 사용자의 URL를 변경하려고 합니다.
Line 4:20: React Hook "useNavigate" is called in function "redirect" that is neither a React function component nor a custom React Hook function. React component names must start with an uppercase letter. React Hook names must start with the word "use" react-hooks/rules-of-hooks
// 위의 에러를 번역했습니다.
리액트 함수 컴포넌트도 아니고 커스텀 훅 함수도 아닌 "redirect" 함수에서 리액트 훅 "useNavigate"가 호출되었습니다. 리액트 컴포넌트 이름은 대문자로 시작해야 합니다. 리액트 훅 이름은 "use"라는 단어로 시작해야 합니다. react-hooks/rules-of-hooks
침착하게 한글 오류를 읽고 생각을 해보도록 합시다.
"useNavigate" 리액트 훅이 "redirect" 함수에서 호출되었지만, "redirect" 함수는 리액트 함수 컴포넌트도 아니고 커스텀 리액트 훅도 아닙니다.
오류 메시지가 친절하게 알려주듯이, 리액트 컴포넌트 이름은 반드시 대문자로 시작해야 하고, 리액트 훅 이름은 반드시 "use"이라는 단어로 시작해야 합니다.
그러니까 함수 컴포넌트로 변경하던지 커스텀 훅 명명 규칙을 따라달라는 거죠. 거기다가 너무 친절하게 인스타공지 아니 공식문서 숙지를 부탁하는 군요. (참고로 몇달전에 React 공식문서가 새단장을 했습니다)
공식문서는 두 가지의 룰을 지켜달라고 합니다.
✅ 최상위 레벨에서만 훅 호출
✅ 리액트 함수에서만 훅 호출
공식문서를 읽어보시면 루프, 조건문 또는 중첩 함수 내부에서 호출하지 말고 초기 리턴이 오기전에 최상위 수준에서 호출하라고 합니다. 컴포넌트가 렌더링될 때마다 훅이 같은 순서로 호출되는 것을 보장할 수 있다고 하네요.
공식문서에 있는 예시를 가지고 왔습니다.
// 🔴 조건문 내부에서 useEffect라는 훅을 호출한 나쁜 예시
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}
// 👍 useEffect라는 훅 내부에서 조건문을 사용하는 좋은 예시
useEffect(function persistForm() {
if (name !== '') {
localStorage.setItem('formData', name);
}
});
훅을 사용할 수 있는 함수에서만 사용하라는 이야기입니다. 아래 두 규칙을 지키는 함수에서만 가능합니다.
✅ 리액트 함수 컴포넌트에서 훅을 호출합니다. (대문자로 시작하는 함수 컴포넌트)
✅ 커스텀 훅에서 훅을 호출할 수 있습니다. (useRedirect ⭕️, redirect ❌)
함수 컴포넌트 또는 use~~~로 시작하는 함수(우리는 커스텀 훅이라고 부르기로 했어요)에서만 사용할 수 있다고 하네요.
모듈화를 하겠다고 만든 코드가 알고보니 커스텀 훅인 상황이 된겁니다.
이제 근본 원인을 파악했으니 오류를 해결해보도록 하겠습니다. 이 오류를 해결하려면 두 가지 옵션이 있습니다:
"redirect"를 리액트 함수 컴포넌트로 변환하기: 1.
사용자 정의 리액트 훅 생성하기: 2.
"redirect" 함수의 이름을 "useRedirect"와 같이 훅의 명명 규칙을 따르도록 바꿉니다.
파일명도 야심차게 바꾸었습니다.
// useRedirect.js
export const useRedirect = (url) => {
const navigate = useNavigate();
navigate(url, { replace: true });
};
// 사용하는 컴포넌트
import { redirect } from "../redirect";
if (사용자정보) {
redirect("/");
}
아…. 조건문에서 사용하지 말라고 했지…. 원하는 방향이지 사용할 수 없군요
그렇다면 조건문이 아닌 곳에서도 저렇게 사용할 수 있을까요?
정답은 “아니요”입니다.
const navigate = useNavigate();
이것 또한 커스텀 훅이기 때문에 룰을 지켜야하기 때문에 안되는거죠.
즉, 이 모듈을 사용할 수가 없습니다. 아래와 같이 사용하는 컴포넌트마다 훅을 만들어줘야합니다.
import { redirect } from "../redirect";
const navigate = useNavigate();
if (사용자정보) {
navigate('/', { replace: true });
}
네. 제목이 곧 내용이었습니다. 오류는 해결했지만 문제해결을 못하게 된 거지요.
const navigate = useNavigate();
이것을 없애고 사용하려고 했지만 안타깝네요. 그래도 커스텀 훅을 만드는 규칙과 공식문서를 한번이라도 더 보게 되는 이벤트가 발생했네요. 만….족 못하지만 만족합니다 😂