결론 : setState도 동등 비교 같은거 하는줄 알았는데 안하는줄 알았는데 한다.
setState를 Hooks 평문에 쓰면 안된다
왜 안될까?
아래 코드를 실행하면 무한 루프를 돌다가 종료된다.
왜 무한루프를 돌까?
setter 함수가 리렌더를 발생시킨다.
setter 함수에는 동일값인지 비교하는 부분이 없다. setter가 호출되면 무조건 리렌더가 발생한다.
중간중간에 초기값이 들어갔다 나오는 이유는 알수 없음.
import React,{useState, useEffect} from 'react';
export function App(props) {
const [state, setState] = useState(10);
useEffect(()=>{
setState(1);
console.log("init: " + state);
},[]);
console.log(state);
setState(2);
return (
<div>{state}</div>
);
}
실행 결과
10
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
10
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
at renderWithHooks (<anonymous>:12170:25)
at mountIndeterminateComponent (<anonymous>:15575:23)
at beginWork (<anonymous>:16592:24)
at HTMLUnknownElement.callCallback (<anonymous>:3336:24)
at Object.invokeGuardedCallbackDev (<anonymous>:3363:26)
at invokeGuardedCallback (<anonymous>:3397:41)
at beginWork$1 (<anonymous>:20013:17)
at performUnitOfWork (<anonymous>:19458:22)
at workLoopSync (<anonymous>:19392:15)
10
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
10
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
2
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
at renderWithHooks (<anonymous>:12170:25)
at mountIndeterminateComponent (<anonymous>:15575:23)
at beginWork (<anonymous>:16592:24)
at HTMLUnknownElement.callCallback (<anonymous>:3336:24)
at Object.invokeGuardedCallbackDev (<anonymous>:3363:26)
at invokeGuardedCallback (<anonymous>:3397:41)
at beginWork$1 (<anonymous>:20013:17)
at performUnitOfWork (<anonymous>:19458:22)
at workLoopSync (<anonymous>:19392:15)
The above error occurred in the <App> component:
at App (<anonymous>:37:55)
Consider adding an error boundary to your tree to customize error handling behavior.
Visit https://reactjs.org/link/error-boundaries to learn more about error boundaries.
Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
at renderWithHooks (<anonymous>:12170:25)
at mountIndeterminateComponent (<anonymous>:15575:23)
at beginWork (<anonymous>:16592:24)
at beginWork$1 (<anonymous>:20001:24)
at performUnitOfWork (<anonymous>:19458:22)
at workLoopSync (<anonymous>:19392:15)
at renderRootSync (<anonymous>:19370:17)
at recoverFromConcurrentError (<anonymous>:18968:30)
at performConcurrentWorkOnRoot (<anonymous>:18915:32)
아래 코드 실행 결과를 확인해보자
밑에서 알수 있는 사실
- 여러번 호출되면 마지막 값이 세팅된다.
- Hooks는 등록된 순서대로 실행된다.
import React,{useState, useEffect} from 'react';
export function App(props) {
const [state, setState] = useState(10);
console.log('rendered');
useEffect(()=>{
setState(3);
console.log("dep1: " + state);
}, [state]);
useEffect(()=>{
setState(1);
console.log("init: " + state);
},[]);
useEffect(()=>{
setState(2);
console.log("dep2: " + state);
}, [state]);
return (
<div>{state}</div>
);
}
// Log to console
console.log('Hello console')
실행 결과
Hello console
rendered
dep1: 10
init: 10
dep2: 10
rendered
dep1: 2
dep2: 2
rendered
useEffect의 의존성 배열은 실행할지 말지 판단할 때 동등비교를 수행한다.
밑에서 알수 있는 사실
- 마지막 설정된 값이 동일한 값이어서 초기화 이후에 추가 리렌더가 발생하지 않았음
import React,{useState, useEffect} from 'react';
export function App(props) {
const [state, setState] = useState(10);
console.log('rendered');
useEffect(()=>{
setState(1);
console.log("init: " + state);
},[]);
useEffect(()=>{
setState(3);
console.log("dep1: " + state);
}, [state]);
useEffect(()=>{
setState(10);
console.log("dep2: " + state);
}, [state]);
return (
<div>{state}</div>
);
}
// Log to console
console.log('Hello console')
실행 결과
Hello console
rendered
init: 10
dep1: 10
dep2: 10
rendered
추가
- 인줄 알았는데 초기값 10에서 10으로 setState 하니까 리렌더가 안됐다.
- 아하...
- 그냥 밖에서 했을때 무한루프 도는건 그냥 리액트 규격 외라서 이상하게 동작하는 거였나보다.
import React,{useState, useEffect} from 'react';
export function App(props) {
const [state, setState] = useState(10);
useEffect(()=>{
setState(10);
console.log("init: " + state);
},[]);
console.log("rendered: " + state);
return (
<div>{state}</div>
);
}
실행결과
rendered: 10
init: 10
그럼 동일값 비교는 어디서 되는가?
- shouldComponentUpdate
- React.memo
- 의존성 배열 - userEffect, useMemo, useCallback
'스터디 > 2024 - 리액트 스터디' 카테고리의 다른 글
Strict Mode 와 useEffect의 cleanUp 함수 사례 (0) | 2024.07.13 |
---|---|
3.1.10 useDebugValue (0) | 2024.07.13 |
3.1.9 useLayoutEffect (0) | 2024.07.13 |
useState, useEffect 동작 퀴즈 2 (0) | 2024.07.06 |
useState, useEffect 동작 퀴즈 1 (0) | 2024.07.06 |
댓글