Description
When the Agenda component is mounted with a selected date (e.g. today), then the user navigates to a different date, items from the original date appear under the wrong date section header. The symptoms shift depending on which date is selected, making the bug look like a data problem rather than a rendering one.
Root cause
Two issues combine to produce the bug:
1. Reservation.shouldComponentUpdate ignores item content when date is undefined
// reservation-list/reservation.js
if (!d1 && !d2) {
changed = false;
}
Only the first item in each day's group receives a date prop; all subsequent items always have date: undefined. When the list shifts to a new selectedDay, React reuses existing Reservation instances (matched by key). For any reused instance where both the old and new date are undefined, shouldComponentUpdate returns false — even when the actual item (reservation content) has changed. The component keeps rendering the stale item.
2. The default keyExtractor produces non-unique keys for custom item types
keyExtractor = (item, index) => {
return this.props.reservationsKeyExtractor?.(item, index) || `${item?.reservation?.day}${index}`;
};
item.reservation.day is undefined for any item type that doesn't match the built-in AgendaEntry shape ({ name, height, day }). This causes all keys to be "undefined0", "undefined1", etc. — purely positional — so React reuses the wrong component instances when the list shifts, triggering the shouldComponentUpdate bug above.
Steps to reproduce
- Mount
<Agenda selected="2024-01-05" items={...} /> where the items for 2024-01-05 have multiple entries (e.g. 4 items) and the previous day (2024-01-04) also has items.
- Use custom item objects that don't have a
day property (i.e. anything other than the built-in AgendaEntry shape).
- Navigate to
2024-01-04 (e.g. via onDayPress).
- Observe that items from
2024-01-05 appear in the 2024-01-04 section.
Expected behaviour
Items for 2024-01-04 appear under the 2024-01-04 section header after navigation.
Actual behaviour
Some items from 2024-01-05 remain visible under the 2024-01-04 section header. The number of incorrect items equals the number of non-first items in the original day's group (i.e. items 2–N, which all have date: undefined).
Workaround
Pass a reservationsKeyExtractor prop that returns a unique key per item. This forces React to create fresh Reservation instances when the list shifts, bypassing the shouldComponentUpdate guard entirely:
<Agenda
reservationsKeyExtractor={(item, index) => item?.id ?? String(index)}
...
/>
Where item is your agenda item object. Any field that uniquely identifies the item works — item?.id, item?.name, etc.
Proposed fix
shouldComponentUpdate should include the item content in its change check when date is undefined, not short-circuit to false:
shouldComponentUpdate(nextProps) {
const d1 = this.props.date;
const d2 = nextProps.date;
if (!d1 && !d2) {
// date didn't change, but the item content might have
if (isFunction(this.props.rowHasChanged)) {
return this.props.rowHasChanged(this.props.item, nextProps.item);
}
return this.props.item !== nextProps.item;
}
// ... existing logic
}
Version
react-native-calendars 1.1314.0
Environment
- Platform: iOS
- react-native-calendars: 1.1314.0
- React Native: 0.79.6
- Expo SDK: 53.0.27
Description
When the Agenda component is mounted with a selected date (e.g. today), then the user navigates to a different date, items from the original date appear under the wrong date section header. The symptoms shift depending on which date is selected, making the bug look like a data problem rather than a rendering one.
Root cause
Two issues combine to produce the bug:
1.
Reservation.shouldComponentUpdateignores item content when date is undefinedOnly the first item in each day's group receives a
dateprop; all subsequent items always havedate: undefined. When the list shifts to a newselectedDay, React reuses existingReservationinstances (matched by key). For any reused instance where both the old and new date areundefined,shouldComponentUpdatereturnsfalse— even when the actual item (reservation content) has changed. The component keeps rendering the stale item.2. The default
keyExtractorproduces non-unique keys for custom item typesitem.reservation.dayisundefinedfor any item type that doesn't match the built-inAgendaEntryshape ({ name, height, day }). This causes all keys to be"undefined0","undefined1", etc. — purely positional — so React reuses the wrong component instances when the list shifts, triggering theshouldComponentUpdatebug above.Steps to reproduce
<Agenda selected="2024-01-05" items={...} />where the items for2024-01-05have multiple entries (e.g. 4 items) and the previous day (2024-01-04) also has items.dayproperty (i.e. anything other than the built-inAgendaEntryshape).2024-01-04(e.g. viaonDayPress).2024-01-05appear in the2024-01-04section.Expected behaviour
Items for
2024-01-04appear under the2024-01-04section header after navigation.Actual behaviour
Some items from
2024-01-05remain visible under the2024-01-04section header. The number of incorrect items equals the number of non-first items in the original day's group (i.e. items 2–N, which all havedate: undefined).Workaround
Pass a
reservationsKeyExtractorprop that returns a unique key per item. This forces React to create freshReservationinstances when the list shifts, bypassing theshouldComponentUpdateguard entirely:Where
itemis your agenda item object. Any field that uniquely identifies the item works —item?.id,item?.name, etc.Proposed fix
shouldComponentUpdateshould include the item content in its change check whendateisundefined, not short-circuit tofalse:Version
react-native-calendars1.1314.0Environment