[Error/TypeScript, Redux] Warning: Cannot update a component (`A`) while rendering a different component (`B`). To locate the bad setState() call inside `B`, follow... (dispatch 사용 시 useEffect 사용하기)

keynene·2023년 11월 26일
0

세팅/오류 Tips

목록 보기
11/11
post-thumbnail

이번 에러도 이 전 포스팅과 같이 TypeScript와 Redux로 개발하던 중 발생한 에러이다.
다시 한 번 느끼지만 TypeScript는 내가 모르고 지나쳤던 나의 실수들을 바로잡고 더 성장하는 데 큰 도움을 주는 것 같다.

우선 에러가 발생하면 에러내용을 잘 살펴봐야 한다.

  1. B 컴포넌트를 렌더링 하는 동안 A 컴포넌트를 업데이트 할 수 없다.
  2. B 컴포넌트 안의 잘못된 setState() 호출을 찾고 수정해라.

아래에서 천천히 해결해보자




B컴포넌트 안의 잘못된 setState() 호출 찾기

다행히 나는 컴포넌트 분리 및 리팩토링을 다 해놓은 상태에서 해당 에러를 발견해서 어느 부분에서 해당 에러가 발생했는지 바로 찾을 수 있었다.

나는 setState()가 아닌, useDispatch에서 에러가 발생하고 있었다.

(TypeScript로 해당 에러를 발견할 수 있었지만, TypeScript조차 일부 에러는 어디서 에러가 발생했는지는 알려주지 않으므로 에러코드를 놓치기 쉬우니, 프론트엔드 개발자라면 개발자 도구를 수시로 확인하자)


해당 에러는 setState()호출에서 발생한다고 하지 않았나

에러코드에서는 setState()로 명시되어 있었기 때문에, 만약 코드가 긴 상황이었다면 해당 에러 발생지점이 useDispatch라는 것을 찾는데 시간이 걸렸을 것 같았다.

그리고 useDispatch또한 Redux 내의 state를 변경하는 함수이므로 내부적으로는 setState()사용 또는 그 역할을 하기 때문에 해당 에러가 발생할 수 있다.


코드를 보고 에러 발생 지점을 찾아보자

import { changePage, pageTitleInterface } from "store/store";

function Shop(){
  let dispatch = useDispatch();
  let headerInfo: pageTitleInterface = {
    page: 'shop',
    title: 'iPad 쇼핑하기',
    subTitle: '',
  }
  
  dispatch(changePage(headerInfo))  //////←에러발생지점

  return(
    <div id="shop">
      //중략...
    </div>
  )
}

위 코드를 살펴보면 저장소 store.ts에서 changePage라는 함수를 가져와 dispatchheaderInfo내용을 changePage의 파라미터로 Shop 컴포넌트를 렌더링 하는동안 전달하고 있는 것을 확인할 수 있다.

👉🏻 여기서 중요한 점은 렌더링 하는 동안 dispatchchangePage를 통해 store.ts에 저장되어있는 어느 한 state를 변경하고 있다는 것이다.


이 에러에 대해서 알아보니, React GitHub에서도 언급했었고 아래 내용에 대해서도 명시되어 있었다.

  1. 렌더링하는 동안 setState를 사용하면 안 됩니다.
  2. 해당 함수 구성 요소는 본질적으로 클래스 구성 요소 렌더링 메서드와 동일합니다.

즉, 렌더링할 동안에는 setState()든 useDispatch()든 state 변경을 하지말란 소리다.




useEffect를 이용해서 state변경을 렌더링 이후로 미루기

나는 해당 useDispatch를 컴포넌트 주기에서 최초 1회만 실행하면 되기 때문에, useDispatch코드를 useEffect안으로 넣어주려고 한다.

👉🏻 useEffect 사용법, React의 LifeCycle

*만약 setState()로 해당 에러를 만났다면 setState()를 렌더링 이후로 변경해주면 된다.


코드를 수정해보자

import { useEffect } from "react"
import { changePage, pageTitleInterface } from "store/store";

function Shop(){
  let dispatch = useDispatch();

  
  useEffect(()=>{
    let headerInfo: pageTitleInterface = {
      page: 'shop',
      title: 'iPad 쇼핑하기',
      subTitle: '',
    }
    dispatch(changePage(headerInfo))
  },[dispatch])

  return(
    <div id="shop">
      //중략 ..
    </div>
  )
}



📚 state 변경은 렌더링 이후에 하도록 설계하자

항상 돌이켜보면 뭐든 설계단계가 제일 중요한 것 같다.

이번 에러도 어떻게 보면 설계미스에서 비롯된 에러였고,
React 컴포넌트의 LifeCycle과 state의 불변성 등 고려할 점이 많은 것 같다.

이러한 React의 특성을 잘 고려해서 설계하는 습관을 들이자.

  1. state렌더링 이후 변경하자

  2. 특정 상황이 아닌 특정 시점state변경이 필요하다면 useEffect를 사용하자

  3. 특정 상황에 변경해야 한다면 onClick, onMouseLeave 등 함수를 사용하자

profile
keynene

0개의 댓글