CalendarPicker icon indicating copy to clipboard operation
CalendarPicker copied to clipboard

Infinite setState

Open elliscwc opened this issue 5 years ago • 6 comments

Hi,

There has been crash reports recently that points to https://github.com/stephy/CalendarPicker/blob/ab5bff9f9761c2f8b40e173737acec63a1123963/CalendarPicker/index.js#L102 with this error message Invariant Violation: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

I suspect it has a chance to happen by changing the month in the calendar

elliscwc avatar Feb 20 '19 07:02 elliscwc

That bug looks like it could happen when the initialDate prop changes. Is your app changing that prop? If possible, please post your <CalendarPicker> usage code.

peacechen avatar Feb 20 '19 15:02 peacechen

This is the usage code, here we don't use initialDate but selectedStartDate instead

  <React.Fragment>
    <CalendarPicker
      ref={setCalendar}
      weekdays={['S', 'M', 'T', 'W', 'T', 'F', 'S']}
      selectedStartDate={moment(selectedDay.format('YYYY-MM-DD'))}
      onDateChange={onDateChange}
      onMonthChange={onMonthChange}
      textStyle={styles.text}
      todayTextStyle={{ color: selectedIsToday ? '#fff' : theme }}
      selectedDayColor={theme}
      customDatesStyles={selectedStyle}
      minDate={new Date()}
    />
    <PrevButton onPress={this.onPrevMonth} theme={theme} show={showPrevMonth} />
    <NextButton onPress={this.onNextMonth} theme={theme} />
  </React.Fragment>
  onPrevMonth = () => {
    this.calendar.handleOnPressPrevious();
  };

  onNextMonth = () => {
    this.calendar.handleOnPressNext();
  };

elliscwc avatar Feb 21 '19 06:02 elliscwc

How confident are you in the accuracy of the crash reporting? updateMonthYear() is only called if the initialDate prop changes, but you're not passing in that prop. Are you by chance calling updateMonthYear() via the ref?

Edit: If the crash report line number is offset, it might be a bug with the selectedStartDate change detection code. A fix hasn't been submitted, but try the change I recommended at: https://github.com/stephy/CalendarPicker/issues/122#issuecomment-465623797

Edit2: For better performance, don't convert the date to string when passing to selectedStartDate:

selectedStartDate={moment(selectedDay.format('YYYY-MM-DD'))}

... Instead, keep it as a Moment object:

selectedStartDate={selectedDay}

peacechen avatar Feb 21 '19 06:02 peacechen

It's quite accurate as we fixed a few issues from there before, maybe it is not pointing the right line this time. Also this issue happens just a few number in production, no repro on our own. And we don't call updateMonthYear() anywhere. Will get back to here after I research and test a bit more...

elliscwc avatar Feb 21 '19 06:02 elliscwc

I've been using this lib on production, I've found this issue:

Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
...
  at componentDidUpdate(node_modules/react-native-calendar-picker/CalendarPicker/DaysGridView.js:74:12)
...  

This is the code:

componentDidUpdate(prevProps) {
    // Optimize re-renders by checking props, with special handling for selected dates.
    // Shallow compare prop changes, excluding selected dates.
    const propDiffs = Utils.shallowDiff(this.props, prevProps, ['selectedStartDate', 'selectedEndDate']);
    if (propDiffs.length) {
      // Recreate days
      const monthSettings = this.initMonthSettings(this.props);
      this.setState({
        monthSettings,
        daysGrid: this.generateDaysGrid(monthSettings),
      });
    }
    ...

I'm not sure if I'm using the lib in the wrong way, I will investigate more on my side. Hope it helps!

igormontella avatar Apr 27 '21 14:04 igormontella

Start with the sample app in this project to repro the issue. Paste the <CalendarStrip> here along with all methods and state passed to it. https://github.com/stephy/CalendarPicker#sample-application

Maximum update depth exceeded errors usually occur with circular state-callback updates. e.g. selectedStartDate is set to a state var, onDateChange updates that same state var, which triggers a re-render due to selectedStartDate's prop changing. Remember that cloning a date is detected as a change, even if the date value is exactly the same.

peacechen avatar Apr 27 '21 15:04 peacechen