CalendarPicker
CalendarPicker copied to clipboard
Infinite setState
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
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.
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();
};
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}
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...
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!
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.