불필요한 리렌더링 막기

SeungMin·2022년 9월 21일
0

JAVA SCRIPT STUDY

목록 보기
3/9

리렌더링의 발생조건

리렌더링은 state, props가 변할때 발생한다.

부모 컴포넌트의 state를 자식의 props로 넘겨줬다면,
해당 state(props)가 변경된다면 부모,자식 컴포넌트 모두 리렌더링 되는것.

그렇다면 고정된 내용을 가진 함수를 props로 넘겨주는 경우는 어떨까?

function parent () {
  const [variable, setVariable] = useState('초기값');
  
  const btnClick = () => {
    console.log("버튼이 클릭됐습니다.")
  }
  
  useEffect(() => {
  	setTimeout(() => {
      setVariable('변경값');
    },5000)
  },[])
  
  return(
    <>
      <FirstChild value={variable} />
      <SecondChild functionProps={btnClick} />
    </>
  );  
}  

해당 코드의 Flow를 설명하면 아래와 같다.

  1. 부모 컴포넌트가 렌더링되며 자식 컴포넌트까지 전부 렌더링된다 (최초)

  2. useEffect에 의해서 FirstChild 컴포넌트에 넘겨준 state(props)가 변경되며 리렌더링이 발생한다.

  3. 발생한 리렌더링은 부모의 state를 트리거로 발생했기 때문에 부모,자식 컴포넌트 모두 리렌더링된다.

근데 그렇다면 SecondChild 컴포넌트는 왜 리렌더링 된것일까?

props로 넘겨받은 함수는 고정된 값이다, 고로 변하지 않았다.

하지만 변했기 때문에 리렌더링 되었다,
정확히는 함수를 참조하는 참조값이 변했기 때문에 리렌더링 된것이다.

부모 컴포넌트의 state가 변했기 때문에 부모 컴포넌트가 리렌더링 되며
부모 컴포넌트에서 선언한 함수 부분도 다시 렌더링되었고,
결과적으로 참조값이 달라지게 된것이다.

그럼 참조값이 달라지지 않게 막을수는 없을까?

있다.


useCallback

useCallback은 인자로 넘겨주는 함수의 내용이 렌더링 되기 이전과 같다면
참조값을 유지시켜주는 React Hook이다

function parent () {
  const [variable, setVariable] = useState('초기값');
 
  const btnClick = () => {
    console.log("버튼이 클릭됐습니다.")
  }
  
  const memorizedBtnClick = useCallback(() => btnClick(),[])
  
  useEffect(() => {
  	setTimeout(() => {
      setVariable('변경값');
    },5000)
  },[])
  
  return(
    <>
      <FirstChild value={variable} />
      <SecondChild functionProps={memorizedBtnClick} />
    </>
  );  
}  

위의 코드처럼 props로 넘겨줄 함수를 useCallback Hook으로 감싸서 선언한뒤 해당 함수를 넘겨주면 된다.

그럼 참조값이 변하지 않는 함수를 넘겨줄수 있게된다.

하지만 아직도 리렌더링이 발생한다, 왜일까?


Render Phase, Commit Phase

해당 주제를 다루기 앞서 간단하게 짚고 넘어갈 예정이기에
자세한 내용은 이곳 을 참고해주세요.


먼저 render phase
컴포넌트를 렌더링하고 변경사항을 계산하는 모든 작업을 처리하는 페이즈이다.

해당 과정에서는

state(props)의 변경이 일어나고 리렌더링이 되는과정에서
컴포넌트를 호출하여 react-elemets를 반환받고,
해당 값을 근거로 새로운 Virture Dom 을 생성한다.

만약 이번이 첫 렌더링이 아니라면,
이전 Virture Dom과 현재의 Virture Dom을 비교하여
Real Dom에 변경이 필요한 목록들을 확인한다.

다음으로 commit phase
돔에 변경사항을 적용하는 과정이다.

만약 변경된 사항이 없다면 해당 과정은 스킵된다.


요약하자면 참조값을 유지하여 변경된 값이 없도록 해주었기 때문에
현재 commit phase를 스킵할수 있는 상태가 되었지만
render phase는 여전히 발생하는 상태이다.

그렇다면 render phase를 막을수 있는 방법은 없을까?


react.memo

react.memo는..

전달받은 props의 값이 이전에 전달받은 값과 동일하다면
컴포넌트의 리렌더링을 막아주고
마지막으로 리렌더링 되었던 결과를 재사용하는 고차 컴포넌트입니다.

사용법은

function SecondChild({memoreizeBtnClick}) {
  return(
    <button onClick={() => memoreizeBtnClick()} />
  );
}  

export default react.memo(SecondChild);

위의 코드처럼 컴포넌트를 export 할 때 사용합니다.

이제 SecondChild 컴포넌트의 리렌더링 과정에서
render phasecommit phase 모두 막았기 때문에
리렌더링이 더이상 발생하지 않게 되었습니다.

그럼 넘겨주는 state(props)가 상수라면 어떨까요?


useMemo

사용방법과 원리는 이전 목차에서 다룬 주제인 useCallback과 동일합니다.

다만 사용되는 대상이 변수(상수) 라는 점이 다릅니다.

??? : 아니 상수데이터면 값이 원래 안변하니까 렌더링 안되는거 아니에요?

네 아닙니다.

해당 변수(상수) 가 선언된 부모 컴포넌트가 리렌더링 된다면,
위에서 다룬 함수의 경우와 똑같이 참조값이 바뀌게됩니다.
고로 리렌더링이 발생합니다.


마치며..

위의 렌더링 최적화 함수,컴포넌트는 무분별하게 오남용하면 안됩니다.
해당 기능 또한 하나의 코드이고 일련의 처리과정이 존재합니다.

무조건 변수의 값이 변하는 상황에서 useMemo를 씌워둔다던가 하는 상황은
오히려 성능을 저하시키는 요인이 될 수 있다는점을 명심해야합니다.

profile
공부기록

0개의 댓글