ERROR Error: Too many re-renders. React limits the number of renders to prevent an infinite loop.
문제 없다고 생각했는데 왜 무한 렌더링이 일어날까
...
const CategoryScroll = () => {
...
const { selectedCategory, setSelectedCategory } = useContext(CategoryContext);
const [orderedCategories, setOrderedCategories] = useState([]);
const { userId, accessToken } = useContext(LoginContext);
const { isLoading, error, data, isSuccess } = useCategoriesQuery(
accessToken,
userId,
);
if (orderedCategories && orderedCategories.length > 0) {
setSelectedCategory(orderedCategories[0].id);
}
if (isLoading) return <Text>Loading...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
if (isSuccess) {
const sorted =
data.length > 1 ? [...data].sort((a, b) => a.id - b.id) : data;
setOrderedCategories(sorted);
}
...
나의 불찰
1. 파이썬 생각하면서 빈 배열은 조건문에서 false인줄 알았다.
=> 아니다! JS에선 array.length로 조건문 내 조건 준다.
2. useEffect 사용을 최대한 지양해야 한다고 해서 최대한 안써보려고 했던게 또 문제가 된 것 같다.
내가 생각한 무한 렌더링이 일어난 흐름
1. useCategoriesQuery에서 data 받아오며 리렌더링
2. 1번이라면isSuccess이므로 setOrderedCategories 이때 리렌더링
3. orderdCategories가 업데이트 되며 if (orderedCategories && orderedCategories.length > 0)를 만족해서 setSelectedCategory 이므로 리렌더링
4. 3번에서 리렌더링 되고 난 후 다시 3번 조건 만족하지만 orderedCategories[0]은 같은 값이니 setState에서 리렌더링 막았을 것
5. 그런데 4번에 이어 여전히 isSuccess는 true 이므로 if문 실행됨
6. 이때 [...data].sort((a, b) => a.id - b.id)가 새로운 리스트 객체를 만들어 내서 setOrderedCategories는 sorted라는 값이 매번 새로운 값이라고 판단하고 계속 리렌더링을 한 것이 아닐까...
GPT와의 문답

현재 내 코드와 에러 코드를 보내줬다.
문제 원인
1. useCategoriesQuery 후 상태 업데이트:
useCategoriesQuery의 결과가 성공적으로 받아지면 orderedCategories 상태를 업데이트하고 있습니다. 이 상태 업데이트는 컴포넌트를 재렌더링하게 만드는데, 만약 useCategoriesQuery 훅이 내부적으로 상태 변경을 감지하여 데이터를 다시 요청한다면, 무한 렌더링이 발생할 수 있습니다.
2. setSelectedCategory의 조건 없는 호출:
컴포넌트가 렌더링될 때마다 orderedCategories 배열이 존재하면 항상 setSelectedCategory가 호출됩니다. 이는 상태를 계속해서 변경하기 때문에 컴포넌트가 무한히 렌더링될 수 있습니다.
해결 방법
1. 상태 업데이트 조건 추가:
상태 업데이트가 필요할 때만 실행되도록 조건을 추가해야 합니다.
2. useEffect 사용:
상태 변화에 따라 한 번만 실행되어야 할 코드에는 useEffect를 사용하여 불필요한 상태 업데이트를 방지합니다.
... 위 코드와 동일
useEffect(() => {
if (isSuccess) {
const sorted = data.length > 1 ? [...data].sort((a, b) => a.id - b.id) : data;
setOrderedCategories(sorted);
}
}, [data, isSuccess]);
useEffect(() => {
if (orderedCategories.length > 0) {
setSelectedCategory(orderedCategories[0].id);
}
}, [orderedCategories]);
실제로
useEffect도 쓸 때는 써야하는가
좀 더 공부해봐야 할 것 같다.
고민하며 찾아본 글들
useEffect에서 useState를 쓰면 벌어지는 일
useEffect 잘못 쓰고 계신겁니다.
찾아만 보고 이해는 잘 못했다.