From b939843249cdfe450e66157f837a4cd350206d0b Mon Sep 17 00:00:00 2001 From: Kyle Pope Date: Tue, 17 Mar 2026 19:48:32 +0800 Subject: [PATCH] Fix review findings: safe date parsing, useCallback discipline, dead class cleanup W-01: Wrap handlePrev/handleNext/handleDayClick in useCallback W-02: Use date-fns parse() instead of new Date() for timezone-safe parsing W-03: Change default firstDayOfWeek from 1 to 0 to match CalendarPage S-01: Use format(day, 'yyyy-MM-dd') as React key instead of toISOString() S-02: Remove dead Tailwind color classes overridden by inline styles Perf: Guard setSelectedDate with comparison to skip no-op re-renders Perf: Memoize selectedDateObj via useMemo to avoid re-parsing each render Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/components/calendar/MiniCalendar.tsx | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/frontend/src/components/calendar/MiniCalendar.tsx b/frontend/src/components/calendar/MiniCalendar.tsx index 123e121..87871d1 100644 --- a/frontend/src/components/calendar/MiniCalendar.tsx +++ b/frontend/src/components/calendar/MiniCalendar.tsx @@ -1,8 +1,8 @@ -import { useState, useEffect, useMemo, memo } from 'react'; +import { useState, useEffect, useMemo, useCallback, memo } from 'react'; import { startOfMonth, endOfMonth, startOfWeek, endOfWeek, eachDayOfInterval, format, isSameDay, isSameMonth, isToday, - addMonths, subMonths, + addMonths, subMonths, parse, } from 'date-fns'; import { ChevronLeft, ChevronRight } from 'lucide-react'; import { Button } from '@/components/ui/button'; @@ -30,10 +30,17 @@ function getOrderedLabels(firstDay: number) { const MiniCalendar = memo(function MiniCalendar({ onDateClick, currentDate, - firstDayOfWeek = 1, + firstDayOfWeek = 0, }: MiniCalendarProps) { + const REF_DATE = useMemo(() => new Date(), []); + + const parseDate = useCallback( + (dateStr: string) => parse(dateStr, 'yyyy-MM-dd', REF_DATE), + [REF_DATE] + ); + const [displayedMonth, setDisplayedMonth] = useState(() => - currentDate ? startOfMonth(new Date(currentDate)) : startOfMonth(new Date()) + currentDate ? startOfMonth(parseDate(currentDate)) : startOfMonth(new Date()) ); const [selectedDate, setSelectedDate] = useState( currentDate ?? null @@ -42,12 +49,12 @@ const MiniCalendar = memo(function MiniCalendar({ // Sync displayed month when main calendar navigates across months useEffect(() => { if (!currentDate) return; - const incoming = startOfMonth(new Date(currentDate)); + const incoming = startOfMonth(parseDate(currentDate)); setDisplayedMonth((prev) => prev.getTime() === incoming.getTime() ? prev : incoming ); - setSelectedDate(currentDate); - }, [currentDate]); + setSelectedDate((prev) => prev === currentDate ? prev : currentDate); + }, [currentDate, parseDate]); const days = useMemo( () => buildGrid(displayedMonth, firstDayOfWeek), @@ -59,10 +66,10 @@ const MiniCalendar = memo(function MiniCalendar({ [firstDayOfWeek] ); - const handlePrev = () => setDisplayedMonth((m) => subMonths(m, 1)); - const handleNext = () => setDisplayedMonth((m) => addMonths(m, 1)); + const handlePrev = useCallback(() => setDisplayedMonth((m) => subMonths(m, 1)), []); + const handleNext = useCallback(() => setDisplayedMonth((m) => addMonths(m, 1)), []); - const handleDayClick = (day: Date) => { + const handleDayClick = useCallback((day: Date) => { const dateStr = format(day, 'yyyy-MM-dd'); setSelectedDate(dateStr); // If clicking a day in another month, also shift the displayed month @@ -70,9 +77,12 @@ const MiniCalendar = memo(function MiniCalendar({ setDisplayedMonth(startOfMonth(day)); } onDateClick(dateStr); - }; + }, [displayedMonth, onDateClick]); - const selectedDateObj = selectedDate ? new Date(selectedDate) : null; + const selectedDateObj = useMemo( + () => selectedDate ? parseDate(selectedDate) : null, + [selectedDate, parseDate] + ); return (
@@ -120,15 +130,15 @@ const MiniCalendar = memo(function MiniCalendar({ return (