개발/토막난 상식

Warning: 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.

RavenKim 2024. 4. 17. 15:28
반응형

문제의 코드

    useEffect(() => {
        if (selectedData !== selectedRowKeys) {
            setSelectedRowKeys(selectedData)
        }
    }, [selectedData])
   useEffect(() => {
        if (verifiedColumns.length > 0) {
            if (useCheckbox) {
                setSelectAddColumns([
                    {
                        //todo 컬럼 추가
                        title: (
                            <>
                                <Checkbox
                                    checked={allChecked}
                                    onChange={(e) => {
                                        if (e.target.checked) {
                                            setSelectedRowKeys(entireKeyList)
                                        } else {
                                            setSelectedRowKeys([])
                                        }
                                    }}
                                />
                            </>
                        ),
                        dataIndex: 'key',
                        key: 'key',
                        width: 35,
                        align: 'center',
                        style: { justifyContent: 'center' },
                        render: (text) => {
                            return (
                                <Checkbox
                                    checked={selectedRowKeys?.includes(text)}
                                    onChange={(e) => {
                                        const keyList = [...selectedRowKeys]
                                        if (e.target.checked) {
                                            keyList.push(text)
                                        } else {
                                            const index = keyList.indexOf(text)
                                            if (index > -1) {
                                                keyList.splice(index, 1)
                                            }
                                        }

                                        setSelectedRowKeys(keyList)
                                    }}
                                />
                            )
                        },
                    },
                    ...verifiedColumns,
                ])
            } else {
                setSelectAddColumns(verifiedColumns)
            }
        }
    }, [verifiedColumns, entireKeyList, allChecked, selectedRowKeys])

 

 

 

 

 

해결 방법

    useEffect(() => {
        // selectedData와 selectedRowKeys가 다르면 업데이트
        if (!arraysEqual(selectedData, selectedRowKeys)) {
            setSelectedRowKeys(selectedData)
        }
    }, [selectedData])

    // 간단한 배열 비교 함수
    function arraysEqual(a, b) {
        if (a === b) return true
        if (a == null || b == null) return false
        if (a.length !== b.length) return false

        for (let i = 0; i < a.length; ++i) {
            if (a[i] !== b[i]) return false
        }
        return true
    }

 

 

첫 번째 코드 조각과 두 번째 코드 조각 사이에는 중요한 차이가 있으며, 이 차이로 인해 두 번째 코드 조각에서 오류가 발생합니다.

첫 번째 코드 조각

첫 번째 코드 조각에서는 arraysEqual이라는 함수를 사용하여 selectedData와 selectedRowKeys 두 배열의 내용이 동일한지를 깊은 비교(deep comparison)로 확인. 이 함수는 두 배열의 길이와 각 요소가 동일한지를 하나하나 확인하여, 실제로 배열의 내용이 같은지를 판단. 따라서, 두 배열이 실제로 같은 내용을 가지고 있으면 상태 업데이트를 하지 않다. 이 방법은 배열의 내용이 같을 때 불필요한 상태 업데이트를 피할 수 있어 효율적이다.

두 번째 코드 조각

반면, 두 번째 코드 조각에서는 selectedData와 selectedRowKeys를 직접 비교하고 있다. 이 경우, JavaScript에서 배열은 참조 타입이기 때문에, 이 두 배열이 같은 객체를 참조하지 않는 한(즉, 메모리 상에서 완전히 동일한 위치를 가리키지 않는 한), 이 조건문은 항상 참(true)으로 평가. 이는 즉, 심지어 두 배열이 내용적으로는 동일하더라도, 다른 메모리 주소를 가리키고 있다면 다른 객체로 간주되어 setSelectedRowKeys(selectedData)가 계속 호출되어 상태 업데이트가 발생하고, 이는 무한 렌더링을 유발할 수 있다.

결론

따라서, 두 번째 코드 조각에서 오류가 발생하는 이유는 배열의 참조 비교로 인해, 실제로 배열의 내용이 변경되지 않았음에도 불구하고 상태 업데이트가 반복적으로 발생하기 때문이다. 배열이나 객체와 같은 참조 타입을 비교할 때는 내용이 동일한지 깊은 비교를 수행해야 하며, 단순히 === 연산자를 사용하는 것으로는 충분하지 않다.