작업한 코드를 모듈화하던 중에 선택한 언어가 bold 표시되는 스타일이 클릭에 따라 제대로 작동하지 않는 것을 발견했다.
'KOR | ENG' 중에 'ENG'을 눌러도 계속 초기 값인 'KOR'에 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)'};
...
`;
추측해본 이유는 세 가지였다.
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체가 잘 적용되었다.