useCallback으로 이슈 해결하기

(아이패드) 앱 테마를 변경한 직후 화면을 가로/세로로 돌렸을 때ㅡ 변경 전 테마로 되돌아가는 이슈
구주애's avatar
Mar 24, 2024
useCallback으로 이슈 해결하기

VoC 중 이런 이슈가 접수되었다.

  1. 앱 다크모드 설정

  2. 화면방향 변경 (가로모드 > 세로모드)

  3. 다크모드 풀림

원인

현재 최상위 컴포넌트의 useEffect에는 아래와 같이 Dimension에 변화가 있을 경우 width, height를 업데이트하는 로직으로 구현되어있다.

즉, 아이패드의 화면 방향 전환과 같이 Dimension 변화가 일어난 경우에는 이 로직이 실행되게 된다. 참고로 context는 전역 state로, theme도 이 context 데이터 중 하나이다.

useEffect(() => {
  Dimensions.addEventListener('change', () => {
     if (!inBackground.current) {
       const {width, height} = Dimensions.get('window');
       resetDimensions();
       setContext({...context, width, height});
     }
  });
}, [])

이 로직에선 변경된 width, height를 업데이트하기 위해 기존 context 및 새로운 width, height값을 넣고 setContext를 실행하지만! 의존성배열에 아무 값도 없는 useEffect에 있는 Dimensions.addEventListner는, 변경된 theme이 업데이트되지 않은 상태이기 때문에 화면방향 전환 전의 theme이 다시 setContext로 들어가게 된다.

해결

  1. addEventListner 이벤트핸들러 내 함수의 추출, useCallback 으로 theme이 업데이트 될 때마다 함수를 다시 생성하도록 함

const handleChangeDimension = useCallback(() => {
   if (!inBackground.current) {
     const {width, height} = Dimensions.get('window');
     resetDimensions();
     setContext({...context, width, height});
   }
}, [context.theme]);
  1. 이벤트 핸들러를 handleDimension 함수 변화에 의존하는 useEffect내로 이동

useEffect(() => {
   Dimensions.addEventListener('change', handleChangeDimension); 
   return () => { 
     Dimensions.removeEventListener('change', handleChangeDimension);
   }; 
}, [handleChangeDimension]);

더 자세히 알아보는 useCallback

원하는 타이밍(두번째인자 값 변화)에 첫번째 인자로 들어간 함수를 새로 만들어 실행시키기 위한 것 (특정 함수를 재사용)

  • 의존성배열 (두번째인자)에 있는 값이 변화되었을 때,

    • 첫번째 인자 함수를 새로 생성해 반환

  • 메모이제이션된 함수를 반환

  • 자주 렌더링되는 컴포넌트에서 함수 최적화하고, 불필요한 함수 재생성 방지 시 사용

→ 메모이제이션이란? 기존에 수행한 연산의 결과값을 어딘가에 저장해두고 동일한 입력이 들어오면 재활용하는 프로그래밍 기법

형태

const function = useCallback(함수, [인자])

첫번째 인자로 넘어온 함수를, 두번째 인자가 변경될 때까지 저장(재사용)

두번째 인자 외 다른 state가 변경될 때에는 함수를 재생성하지 않는 듯함

어떤 A, B함수는 동일한 형태로 동일한 값을 반환한다 하더라도 참조가 다르기 때문에 A, B는 같다고 할 수 없음. 특정 state 업데이트로 인한 컴포넌트 리렌더링 시 함수가 다시 그려질 때 해당 함수를 사용하는 하위 컴포넌트가 리렌더링되는 것 역시 그와 같은 이유 때문! 즉, 함수는 값이 아닌 참조로 비교되기 때문임

추가로 자식컴포넌트에 함수를 props으로 줄때는 반드시 useCallback을 사용하여 리렌더링이 안되도록 해야 한다.

Share article

KooLog