useEffect 사용하는거 어렵다

고병찬·2024년 7월 31일

TIL

목록 보기
8/54

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 종속성
https://hanna-log.tistory.com/m/entry/useEffect-%EC%A2%85%EC%86%8D%EC%84%B1%EC%9C%BC%EB%A1%9C-%EC%B6%94%EA%B0%80%ED%95%A0-%ED%95%AD%EB%AA%A9-%EB%B0%8F-%EC%B6%94%EA%B0%80%ED%95%98%EC%A7%80-%EC%95%8A%EC%9D%84-%ED%95%AD%EB%AA%A9-1

고민하며 찾아본 글들
useEffect에서 useState를 쓰면 벌어지는 일
useEffect 잘못 쓰고 계신겁니다.

찾아만 보고 이해는 잘 못했다.

profile
안녕하세요, 반갑습니다.

0개의 댓글