Instagram clone - Dashboard를 만들다가 알게 된 사실이다.
Dashboard 컴포넌트에 photos state를 두고 아래로 내려주기 위해 photo랑 following을 dashboard에 옮기려고 했다.
그래서 usePhotos, useUser를 useEffect안에다 실행했다.
그때 아래와 같은 에러가 생겼다.
Uncaught Error: Should have a queue. This is likely a bug in React. Please file an issue.
React has detected a change in the order of Hooks called by Dashboard. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks
rules of Hooks 에도 잘 설명되어있지만 hook을 사용하는 순서가 항상 일정해야한다. (또다른 규칙은 hook이 컴포넌트 함수에서 최상위 위치에 사용되는게 낫다.)
Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function, before any early returns.
By following this rule, you ensure that Hooks are called in the same order each time a component renders. T
hat’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls.
그렇다 컴포넌트 가장 최상단에 선언되어야 하며 그래야 hooks의 실행 순서가 일정해진다.
예를 들어 아래와 같은 순서로 hook이 실행된다고 해보자.
function Form() {
// 1. Use the name state variable
const [name, setName] = useState('Mary');
// 2. Use an effect for persisting the form
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
// 3. Use the surname state variable
const [surname, setSurname] = useState('Poppins');
// 4. Use an effect for updating the title
useEffect(function updateTitle() {
document.title = name + ' ' + surname;
});
// ...
}
그럼 hook의 실행순서는 아래와 같다.
// First render
// ------------
useState('Mary'); // 1. Initialize the name state variable with 'Mary'
useEffect(persistForm); // 2. Add an effect for persisting the form
useState('Poppins'); // 3. Initialize the surname state variable with 'Poppins'
useEffect(updateTitle); // 4. Add an effect for updating the title
// -------------
// Second render
// -------------
useState('Mary'); // 1. Read the name state variable (argument is ignored)
useEffect(persistForm); // 2. Replace the effect for persisting the form
useState('Poppins'); // 3. Read the surname state variable (argument is ignored)
useEffect(updateTitle); // 4. Replace the effect for updating the title
그러나 두번째 hook(useEffect)를 조건문안에 넣는 다고 해보자
// 🔴 We're breaking the first rule by using a Hook in a condition
if (name !== '') {
useEffect(function persistForm() {
localStorage.setItem('formData', name);
});
}
그럼 순서는 아래와 같이 바뀐다.
useState('Mary'); // 1. Read the name state variable (argument is ignored)
// useEffect(persistForm) // 🔴 This Hook was skipped!
useState('Poppins'); // 🔴 2 (but was 3). Fail to read the surname state variable
useEffect(updateTitle); // 🔴 3 (but was 4). Fail to replace the effect
그래서 조건문 안에 hook을 실행하면 안되고 hook안에서 조건문을 실행하는게 더 좋다. 기억하자 항상 순서는 일정해야한다.
useEffect(function persistForm() {
// 👍 We're not breaking the first rule anymore
if (name !== '') {
localStorage.setItem('formData', name);
}
});
그래서 Dashboard page안에서 usePhotos
랑 useUser
를 같이 사용하려고 하다가 충돌이 생긴거다. 그래서 그냥 useUser
를 ProtectedRoute
로 옮겨버리니 문제 해결!