Skip to content

Calendar component very slow when select day on Android #2760

@nvquan-prog

Description

@nvquan-prog

Calendar is very slow when selecting day on Android

Description

The Calendar component experiences significant performance issues when selecting a day on Android devices. There's a noticeable delay (2000-5000ms+) when tapping a day, making the calendar feel unresponsive.

Steps to Reproduce

  1. Open app on Android device
  2. Navigate to screen using Calendar
  3. Tap any selectable day
  4. Observe delay before selection

Expected Behavior

  • Instant, smooth day selection
  • Immediate visual feedback
  • No noticeable lag

Actual Behavior

  • Significant delay (2000-5000ms+) when tapping a day
  • Delayed visual feedback
  • Calendar feels unresponsive
  • Android-specific issue (iOS works fine)

Environment

  • Platform: Android
  • React Native: 0.77.2
  • react-native-calendars: 1.1312.0

Technical Details

Component uses react-native-calendars with custom DayComponent and renderDay callback. Despite existing optimizations (React.memo, useMemo, useCallback), Android still experiences lag during onDayPress execution.

Possible Causes

  1. Heavy date/timezone calculations in onDayPress
  2. Calendar re-rendering on each selection
  3. react-native-calendars Android performance issues
  4. Bridge communication overhead

Impact

  • Poor user experience on Android
  • Calendar feels unresponsive
  • Functionality works but with noticeable lag

My source

<Calendar
          testID={`${testID}-calendar`}
          current={currentMonth}
          minDate={minDateString}
          maxDate={maxDateString}
          onDayPress={onDayPress}
          dayComponent={renderDay}
          theme={calendarTheme}
          renderArrow={renderArrow}
          enableSwipeMonths={false}
          disableMonthChange={false}
          hideArrows={false}
          disableArrowLeft={shouldDisableLeftArrow}
          disableArrowRight={shouldDisableRightArrow}
          hideExtraDays={false}
          onMonthChange={(month: DateData) => {
            setCurrentMonth(month.dateString);
          }}
          style={styles.calendar}
 />

  /**
   * Custom day component renderer
   * Displays day number, lunar calendar info, and peak day indicators
   * Optimized with memoized DayComponent to prevent unnecessary re-renders
   * @param day - Day data from calendar
   */
  const renderDay = useCallback(
    (day: { date?: DateData; state?: string }) => {
      if (!day.date) return null;

      const dateString = day.date.dateString;
      const isSelected = dateString === selectedDate;
      // Use pre-calculated today date string for faster comparison
      const isToday = dateString === todayDateString;

      // Check if date is disabled (before min or after max)
      // Use pre-calculated date strings for faster comparisons
      const isDisabled: boolean =
        day.state === 'disabled' ||
        dateString < minDateString ||
        Boolean(maxDateString && dateString > maxDateString);

      // Calculate isCanNotChoose only if minimumDate exists
      let isCanNotChoose = false;
      if (minimumDate) {
        const dateWithTime = DateTimeHelpers.toDateTz({
          timezone,
          date: dateString,
        })
          .hour(valueHour)
          .minute(valueMinute);
        const todayStart = DateTimeHelpers.toDateTz().startOf('day');
        const minDateMinusOne = DateTimeHelpers.toDateTz({
          timezone,
          date: minimumDate,
        }).subtract(1, 'minute');

        isCanNotChoose = Boolean(
          DateTimeHelpers.checkIsBetween({
            timezone,
            date: dateWithTime,
            startDate: todayStart,
            endDate: minDateMinusOne,
          }),
        );
      }

      // Check if it's a peak day (weekend or holiday)
      // Cache day of week calculation
      const dayOfWeek = DateTimeHelpers.getDayOfWeek({
        timezone,
        date: dateString,
      });
      const isWeekend = dayOfWeek === 0 || dayOfWeek === 6;
      // Only check holiday if not already a weekend (short-circuit optimization)
      const isHoliday =
        !isWeekend && Boolean(checkHoliday(dateString, timezone));
      const isPeakDay: boolean = isWeekend || isHoliday;

      // Get lunar calendar info
      const lunarInfo = getLunaHoliday(dateString, timezone);

      return (
        <DayComponent
          day={day.date}
          isSelected={isSelected}
          isToday={isToday}
          isDisabled={isDisabled}
          isCanNotChoose={isCanNotChoose}
          isPeakDay={isPeakDay}
          lunarInfo={lunarInfo}
          onDayPress={onDayPress}
        />
      );
    },
    [
      selectedDate,
      todayDateString,
      minDateString,
      maxDateString,
      timezone,
      minimumDate,
      valueHour,
      valueMinute,
      onDayPress,
    ],
  );

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions