react-native-calendars icon indicating copy to clipboard operation
react-native-calendars copied to clipboard

dayComponent too many renders

Open babpulss opened this issue 1 year ago • 5 comments

First of all, thank you for creating the react-native calendar. However, there is an issue with the customization of the DayComponent that causes too much rendering

This is due to the function or object comparison that goes into the props of the Calendar component.

It stems from the 'areEqual' function in react-native-calendars/src/calendar/day/index.js:11 not working correctly.

The specific point of misbehavior is In the above-mentioned index.js file, on line 14, the omit function of lodash is incorrectly executed. This is because functions like onPress, onLongPress, or objects specified by theme have different references when compared in the _.some function, so the React.memo function thinks it's a different object every time it's executed, and it doesn't work correctly. So every time you click on a date within the same month, you'll see all 30+ components performing unnecessary operations that render over and over again.

Therefore, we need to add logic to ignore the comparison when comparing functions or objects.

//before 
function areEqual(prevProps, nextProps) {
    const prevPropsWithoutMarkDates = omit(prevProps, 'marking');
    const nextPropsWithoutMarkDates = omit(nextProps, 'marking');
    const didPropsChange = some(prevPropsWithoutMarkDates, function (value, key) {
        return value !== nextPropsWithoutMarkDates[key];
    });
    const isMarkingEqual = isEqual(prevProps.marking, nextProps.marking);
    return !didPropsChange && isMarkingEqual;
}

// after 

function areEqual(prevProps, nextProps) {
    const prevPropsWithoutMarkDates = omit(prevProps, 'marking');
    const nextPropsWithoutMarkDates = omit(nextProps, 'marking');
    const didPropsChange = some(prevPropsWithoutMarkDates, function (value, key) {
        if (typeof nextPropsWithoutMarkDates[key] === 'function') {
            return false;
        }
        if (typeof nextPropsWithoutMarkDates[key] === 'object') {
            return false;
        }
        return value !== nextPropsWithoutMarkDates[key];
    });
    const isMarkingEqual = isEqual(prevProps.marking, nextProps.marking);
    return !didPropsChange && isMarkingEqual;
}

babpulss avatar Apr 17 '24 04:04 babpulss

and after all, I used library code as below

 }) => {
  const today = useRef(dayjs().format('YYYY-MM-DD'));
  const [selectedDate, setSelectedDate] = useState(today.current);
  const [testID, setTestID] = useState(0);

  const onMonthChange = (date: DateData) => {
    onMonthPress(date);
    setTestID(date.month);
  };

  useEffect(() => {
    setTestID(Date.now());
  }, [customParentArrayObject]);

  return (
    <Calendar
      testID={testID.toString()}
      onDayPress={({ dateString }) => {
        onDatePress(dateString);
        setSelectedDate(dateString);
      }}
      markedDates={{
        [today.current]: { today: true },
        [selectedDate]: { selected: true, },
      }}
      renderHeader={renderHeader}
      theme={{
        arrowColor: '#222222',
        arrowStyle: { paddingHorizontal: 53 },
        textSectionTitleColor: '#b6c1cd',
        selectedDayBackgroundColor: '#00adf5',
        todayTextColor: 'blue',
        dayTextColor: 'red',
      }}
      onMonthChange={onMonthChange}
      dayComponent={(props) => <CustomDayComponent customArrayObject={customParentArrayObject} props={props} />}
      hideExtraDays={true}
      disableAllTouchEventsForDisabledDays={true}
      disableAllTouchEventsForInactiveDays={true}
    />
  );
};

babpulss avatar Apr 17 '24 04:04 babpulss

Does anyone know if there is a pull request for this issue?

angelo-bubble avatar Jun 14 '24 16:06 angelo-bubble

is there any solution for this issue

amarouldhamadouche avatar Jul 04 '24 17:07 amarouldhamadouche