💻 개발 이야기/Front-end

[React] Maximum update depth exceeded 에러를 해결한 방법

Jinseong Hwang 2023. 11. 20. 02:31

 

안녕하세요. 황진성입니다.

 

이 글은 지난 글에서 이어집니다.

[React/TS] MUI DataGrid로 수정 가능한 테이블 만들기

 

기능을 개발하며 처음 보는 에러가 발생했는데, 해결하신 분들마다 방식이 제각각이라 제가 해결한 방법도 소개하고자 합니다.

 

 

무슨 에러일까?


가만히 있어도 계속 쌓이는 에러 로그

 

Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render.
# DeepL 번역
최대 업데이트 깊이를 초과했습니다. 컴포넌트가 useEffect 내부에서 setState를 호출하지만 사용 효과에 종속성 배열이 없거나 렌더링 할 때마다 종속성 중 하나가 변경되는 경우 이 문제가 발생할 수 있습니다.

 

즉, setState 함수가 너무 빈번하게 호출된다는 것을 문제 삼고 있습니다.

 

 

나의 해결 방법


세줄 요약

  1. "데이터 변경에 필요한 프로퍼티"를 저장하는 State를 하나 만들었다. (이름 = editProps)
  2. useEffect를 사용했고, editProps 값을 dependency array에 넣어줬다.
  3. 이벤트 발생 시 이전 editProps와 새롭게 생성된 editProps가 다를 경우에만 setState를 호출해준다.

 

첫 번째로, "데이터 변경에 필요한 프로퍼티"를 저장하는 State를 하나 만들었습니다.

type CellEditProps = {
  "rowId": string,
  "colName": string,
  "value": string,
}

const App = () => {
  const [editProps, setEditProps] = useState<CellEditProps | null>(null);
  // ...
}

 

테이블의 데이터를 변경하기 위해 몇 번째 행, 어떤 컬럼, 어떤 값으로 변경되는지에 대한 정보가 필요했습니다. 해당 정보들을 저장하기 위해 CellEditProps라는 type을 만들고 State를 만들어줬습니다. 수정 이벤트가 없는 경우에는 null값이 저장될 수 있도록 타입을 지정해 줬습니다.

 

 

두 번째로, useEffect를 사용하면서 editProps 값을 dependency array에 넣어줬습니다.

useEffect(() => {
  if (editProps === null) return;
  setData(prevState => {
    return prevState.map((row, idx) => {
      if (idx.toString() === editProps.rowId) {
        return {...row, [editProps.colName]: editProps.value};
      }
      return row;
    });
  });
}, [editProps]);

 

useEffect를 사용해서 editProps에 대한 상태 변경이 있을 때만 data State를 변경하도록 했습니다.

 

 

세 번째로, 이전 editProps와 새롭게 생성된 editProps가 다를 경우에만 setState를 호출해 주도록 했습니다.

const handleEditEvent = (e: any) => {
  const newEditProps = parseCellEditProps(e);
  if (JSON.stringify(editProps) !== JSON.stringify(newEditProps)) { // 여기!
    setEditProps(newEditProps);
  }
};

return (
  <div className="App">
    <DataGrid
      sx={{"width": "500px", "margin": "auto"}}
      columns={columns}
      rows={rows}
      onStateChange={handleEditEvent}
    />
  </div>
);

 

사실 이 부분이 핵심입니다.  editProps에는 수정되기 전이라도 여러 이벤트에 의해 중복된 값이 많이 노출됩니다. 같은 값으로 업데이트하는 경우를 막기 위해 내부 값 비교 로직이 추가됐습니다.

 

JavaScript Object 타입을 비교하기 위해 여러 방법이 있지만 저는 가장 간단한 방법인 JSON.stringify()를 통해 문자열로 비교했습니다. 크기가 작은 Object라서 이렇게 비교해도 괜찮다는 생각이 들었습니다.

 


 

도움이 되셨으면 좋겠습니다.

프론트엔드 관련 부족한 점이 많으니 피드백 환영합니다!