[react-i18next] 스타일 이슈 (React에서 리렌더링이 일어나지 않는 경우를 파악하여 문제 해결)

Sheryl Yun·2024년 2월 23일
0
post-thumbnail

작업한 코드를 모듈화하던 중에 선택한 언어가 bold 표시되는 스타일이 클릭에 따라 제대로 작동하지 않는 것을 발견했다.

상세 현상

'KOR | ENG' 중에 'ENG'을 눌러도 계속 초기 값인 'KOR'에 bold가 표시되었다.

기존의 코드는

  • 부모에서 useState로 상태와 setter를 내려주고
  • 자식에서 setter 활용해서 상태 바꿔주기 + 바뀐 상태에 따라 styled-components에 prop을 줘서 선택한 언어 텍스트에 bold를 적용하는 방식이었다.

하지만 모듈화를 하면서 자식으로 useState를 옮겼더니 예상과 달리 언어를 누를 때마다 컴포넌트 리렌더링이 안 되었고, ENG을 누르든 KOR를 누르든 bold체 변화가 일어나지 않았다.

바꾸기 전 코드

export const Language = ({ hideMenu }: LanguageProps) => {
  const [selectedLanguage, setSelectedLanguage] = useState('KOR');
  
   return (
    <Flex>
      <LanguageText
        onClick={() => {
          changeLanguage('ko');
          setSelectedLanguage('KOR');
          hideMenu();
        }}
        $isSelectedLanguage={selectedLanguage === 'ko'}
      >
        KOR
      </LanguageText>
      ...
    );
    
  const LanguageText = styled.span<{ $isSelectedLanguage: boolean }>`
    color: ${({ $isSelectedLanguage }) =>
      $isSelectedLanguage ? 'rgb(255, 255, 255)' : 'rgb(111, 117, 123)'};
    ...
  `;

추측해본 이유는 세 가지였다.

  • i18next의 메서드(changeLanguage) 실행은 리액트 컴포넌트 함수의 리렌더링에 영향을 끼치지 않는다.
  • 언어를 선택한 뒤 전체 부모 컴포넌트를 닫는 hideMenu 함수가 부모에서 전달된 props인데 함수가 실행될 때 props가 변경되지 않기 때문에 해당 컴포넌트의 리렌더링에 영향을 끼치지 않는다.
  • useState의 setter의 값을 바꾼 직후에는 컴포넌트가 리렌더링되지 않는다.

setter 변경과 관련하여
리액트의 렌더링은 렌더링될 때 시점의 상태 값으로 '스냅샷'을 찍은 듯이 진행된다.
따라서 해당 렌더링 때 setter로 인해 바뀐 상태 값은 바로 적용되는 게 아니라 '다음 번 리렌더링' 때 비로소 반영된다. - 공식 문서

즉, 리렌더링이 되어야 setter에 넣어준 바뀐 상태 값이 적용되는데 onClick으로 인한 어떤 변경에도 리렌더링을 일으키는 요소가 없었던 것이다.

useEffect를 써서 '클릭 시 상태 값 변경으로 리렌더링을 강제로 일으키는' 방법을 잠깐 고민하다가 i18next 자체에서 '현재 선택된 언어'를 받아올 수 있을까 하는 생각이 스쳤다.

다행히 해당 방법이 있어서 코드에 적용했더니 useState 없이도 잘 동작했다.

잠깐 있었던 이슈
i18next.language 여서 { language } 식으로 import가 될 수 있을 줄 알았지만 { changeLanguage }와 달리 i18next을 import한 뒤 i18next.language 식으로 꺼내 써야 했다.

수정한 코드

import i18next, { changeLanguage } from 'i18next';

export const Language = ({ hideMenu }: LanguageProps) => {
   return (
    <Flex>
      <LanguageText
        onClick={() => {
          changeLanguage('ko');
          hideMenu();
        }}
        $isSelectedLanguage={i18next.language === 'ko'}
      >
        KOR
      </LanguageText>
      ...
    );
  
  
  // 스타일에선 바뀐 것 없음
  const LanguageText = styled.span<{ $isSelectedLanguage: boolean }>`
    color: ${({ $isSelectedLanguage }) =>
      $isSelectedLanguage ? 'rgb(255, 255, 255)' : 'rgb(111, 117, 123)'};
    ...
  `;

이후 매번 리렌더링이 발생하면서 모달에서 선택된 언어에 bold체가 잘 적용되었다.

profile
데이터 분석가 준비 중입니다 (티스토리에 기록: https://cherylog.tistory.com/)

0개의 댓글