프로젝트에서 DatePicker를 직접 구현하게 되었다.
화살표 아이콘을 누르면 한달씩 뒤 또는 앞으로 넘어가는 구조이다.
date-fns 라이브러리를 사용해서 날을 생성했고 다음달 이전달로 넘어갈때마다 날짜들을 재조정 시켜주었다.
이때 문제가 발생했다.
달이 바뀔 때 마다 다시 렌더링 될때 정상 값들이 아닌 이상한 값들이 달력에 껴있던 것이였다.
몇번 달을 이동하다 보면 첫째주에서 시작해야할 1일은 다른 주에서 나오기 시작했다.
콘솔에 찍어보니 값은 정확했다.
문제는 key 값이었다.
코드를 보니 key값을 중복값으로 입력하고 있었다.
const DatePickerModal = () => {
useEffect(() => {
editCurrentMonth(selectedDate);
}, []);
const {
modalClose,
currentMonth,
selectedDate,
handlePrevMonth,
handleNextMonth,
editCurrentMonth,
} = useContext(DatePickerProvider);
const CalendarDateCards = useMemo(() => {
const monthStart = startOfMonth(currentMonth);
const daysInMonth = getDaysInMonth(currentMonth);
const firstDayOfMonth = getDay(monthStart);
const calendarArray = Array.from({ length: daysInMonth + firstDayOfMonth }, (_, i) => {
if (i < firstDayOfMonth) {
return "";
}
return addDays(monthStart, i - firstDayOfMonth);
});
return calendarArray.map((day) => {
return <DatePickerCalendarCard key={day.toString()} modalClose={modalClose} day={day} />;
});
}, [currentMonth]);
key값으로 Date또는 ''인 day를 toString으로 변환해서 주고 있는데 ''을 toString하면 그대로 이기때문에 중복되는 key값이 생겨버린다.
따라서 key값에 유니크한 값을 주기 위해서 '' 값은 첫째줄에만 나올때니 요일+'' 을 넣어주었고 문제는 해결되었다.
const CalendarDateCards = useMemo(() => {
const monthStart = startOfMonth(currentMonth);
const daysInMonth = getDaysInMonth(currentMonth);
const firstDayOfMonth = getDay(monthStart);
const calendarArray = Array.from({ length: daysInMonth + firstDayOfMonth }, (_, i) => {
if (i < firstDayOfMonth) {
return "";
}
return addDays(monthStart, i - firstDayOfMonth);
});
return calendarArray.map((day, index) => {
return (
<DatePickerCalendarCard
key={weekday[index] + day.toString()}
modalClose={modalClose}
day={day}
/>
);
});
}, [currentMonth]);
🍪 왜 이런 문제가 나타날까?
React는 state나 props가 갱신되면 새로운 React 엘리먼트 트리를 반환한다. 이때 하나의 트리를 가지고 다른 트리로 변환하기 위한 최소한의 연산수는 n개의 엘리먼트가 있는 트리에서 O(n^3)의 복잡도를 가진다.
만약 1000개의 엘리먼트를 그리기 위해서는 10억번의 비교 연산을 해야 한다.
따라서 React는 O(n) 복잡도를 가진 비교 알고리즘을 구현했다.
이때 이용하는 것이 바로 key인데 React는 key를 통해서 기존 트리와 이후 트리의 자식들이 일치하는지 확인하고 효율적으로 수행한다.이렇게 수행하기 위해서는 key값은 무조건 유일한 값이어야 한다. 만약 중복이 된다면 react는 제대로 비교 알고리즘을 사용 할 수 없게 되고 렌더링하는 값들도 문제가 있을 수 있다. 나의 경우가 이 경우인것 같다.
추가
예전에는 나도 그랬고 많은 사람들도 key값을 index값으로 사용하는 경우가 많은데 절대 그러지 말도록 하자
이는 공식 문서에도 친절히 예시까지 보여주며 경고 하고 있다.
인덱스를 key값으로 사용하는 경우 문제가 발생하는 예시
새로운 todo를 추가해주고 내용을 입력한 다음 정렬을 시켜보자. id 는 정렬이 되지만 우리가 기대한 대로 입력한 값까지 정렬되지 않는 모습을 보인다.
인덱스를 key값으로 사용하지 않는 경우
'Frontend' 카테고리의 다른 글
짧게 알아보는 React 19 버전 (0) | 2024.05.10 |
---|---|
[React hook form] 제어 컴포넌트와 비제어 컴포넌트 동시에 사용하기 (0) | 2024.04.03 |
[React] 불필요한 리렌더링을 막아서 6배가 빨라졌다고? (0) | 2024.02.22 |
[Framer motion] width (0) | 2023.10.18 |
React-Query Query data cannot be undefined 해결 방법 및 Promise에 대해 (0) | 2023.07.04 |