오류

배열의 원소 하나만 변경되어도 모든 자식 요소가 리렌더링되는 현상

vamosdev12 2025. 5. 20. 11:41

[문제점]

자식요소에서 부모요소에서 상태관리 중인 배열을 참조하고 있고,

배열의 상태 조작 시(값 변경 시),

해당 상태를 참조하고 있는 자식 요소 전체가 불필요하게 리렌더링 되는 상황이 발생함.

(해당 원소를 참조하는 카드만 리렌더링되어야 함)

 

예)

const ParentComponent = () => {

	const [list, setList] = usetate()

	// list의 원소 하나만 바뀌어도 모든 ChildrenCard가 리렌더링 됨.
	return (
    	<>
            {storeData.map((v: any, i: any) => (
                <ChildrenCard data={list[i]} />
            )}
		</>
	)
}

 

 

[탐구과정]

 

1. Chat GPT에게 열심히 물어봤는데, 리액트는 값의 변화를 감지하기 때문에 배열이 변경되어도 해당 값을 참조하는 해당 컴포넌트만 변경된다고 주야장천 얘기함.... 이로 시간 허비 많이 함.

  => 하지만 결과는 다름. 예를 들어 카드가 10개면, 배열의 모든 값이 한차례 변경되면 10 * 10, = 100회의 리렌더링이 발생함. 

 

2. React.memo() 사용

=> 자식요소를 memo로 저장해두고, 항상성을 유지해야 하는 함수를 해당 (prev, next) => prev.data === next.data 로 설정.

=> 하지만 어차피 부모요소 단에서 '전체가 변경되었음'의 신호를 전파해 버리니 전혀 먹히지 않음^^

 

[해결방안]

 

1.  상태가 아닌 일반 변수를  useRef로 관리

     set 함수 없이, javaScript 코드로 속성 적용

const ParentComponent = () => {

	// useState 대신 useRef로 관리
	 cosnt list = useRef<string[]>([]);
     
     useEffect(()=>{
		if(someFactor){
        //javaScript 코드로 속성 적용
            const img = document.getElementById(`storeCard_${storeIdx}`) as HTMLImageElement | null;
            if(img){
                img.src = list[0];
            }
        }   
     }, [someFactor])

	return (
    	<>
            {storeData.map((v: any, i: any) => (
                <ChildrenCard />
            )}
		</>
	)
}

 

2. 자식 요소 내부에서 상태관리를 하고 업데이트 함.

  => 애초에 필요한 데이터를 모두 내부에서 선언하고, 필요한 useEffect도 모두 내부러 가져감.

const ParentComponent = () => {

    const GetChildrens = () => {
    
        const [list, setList] = usetate()

        useEffect(()=>{
            if (someFactor) {
                setList(newArr)
            }
        }, [someFactor])

		return(
            <>
                {storeData.map((v: any, i: any) => (
                	<ChildreComponent aa={v.aa] bb={v.bb}/>
                )}
            </>
		)
    }

   return( 
    	<>
            <GetChildrens />
		</>
	)
}

 

 

[남은 해결 과제....]

1. 그런데 부모에서 scrollIntoView를 적용하거나, 부모요소에 현재시간 표시 등을 넣는 기능을 사용할 경우,

   또 다시 자식 요소는 전체 리렌더링 된다.

  ( scrollIntoView  코드에 대한 건 미해결,

  현재시간 표시 등 부모 컴포넌트에서 무관한 상태 변화 시 자식 요소 리렌더링 되는 문제는 해결 => forwardRef 참고)