react-native-calendars: Agenda : "shouldComponentUpdate" is not working properly
Description
Hi, When the name of an item for a day has changed (other than the first), the reservation component does not trigger the update.
Expected Behavior
Trigger update for all changed items.
Suggested fix
I think the issue came from : reservation We see that we say do NOT update the reservation if the d1/d2 does not exist. And we know that d1/d2 is a date that only exists for the first reservation item. That’s why I think only my first item is updated.
shouldComponentUpdate(nextProps: ReservationProps) {
const d1 = this.props.date;
const d2 = nextProps.date;
const r1 = this.props.item;
const r2 = nextProps.item;
let changed = true;
if (!d1 && !d2) {
changed = false; // HERE
} else if (d1 && d2) {
if (d1.getTime() !== d2.getTime()) {
changed = true;
} else if (!r1 && !r2) {
changed = false;
} else if (r1 && r2) {
if ((!d1 && !d2) || (d1 && d2)) {
if (isFunction(this.props.rowHasChanged)) {
changed = this.props.rowHasChanged(r1, r2);
}
}
}
}
return changed;
}
Environment
npm ls react-native-calendars
: react-native-calendars@1.1280.0npm ls react-native
: react-native@0.66.4
Device/emulator/simulator & OS version: Android / Galaxy A12 / Android version : 11
Reproducible Demo
I took the example of the demo by adding an action which should modify the name of the clicked item by “Has changed !” Below the code :
import React, {Component} from 'react';
import {StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import {Agenda, AgendaEntry, AgendaSchedule, DateData} from 'react-native-calendars';
interface State {
items?: AgendaSchedule;
}
export default class AgendaScreen extends Component<State> {
state: State = {
items: undefined,
};
render() {
return (
<Agenda
reservationsKeyExtractor={item => `${item.reservation?.day}`}
items={this.state.items}
loadItemsForMonth={this.loadItems}
selected={'2017-05-16'}
renderItem={this.renderItem}
renderEmptyDate={this.renderEmptyDate}
rowHasChanged={this.rowHasChanged}
showClosingKnob={true}
// markingType={'period'}
// markedDates={{
// '2017-05-08': {textColor: '#43515c'},
// '2017-05-09': {textColor: '#43515c'},
// '2017-05-14': {startingDay: true, endingDay: true, color: 'blue'},
// '2017-05-21': {startingDay: true, color: 'blue'},
// '2017-05-22': {endingDay: true, color: 'gray'},
// '2017-05-24': {startingDay: true, color: 'gray'},
// '2017-05-25': {color: 'gray'},
// '2017-05-26': {endingDay: true, color: 'gray'}}}
// monthFormat={'yyyy'}
// theme={{calendarBackground: 'red', agendaKnobColor: 'green'}}
//renderDay={(day, item) => (<Text>{day ? day.day: 'item'}</Text>)}
// hideExtraDays={false}
// showOnlySelectedDayItems
/>
);
}
loadItems = (day: DateData) => {
const items = this.state.items || {};
setTimeout(() => {
for (let i = -15; i < 85; i++) {
const time = day.timestamp + i * 24 * 60 * 60 * 1000;
const strTime = this.timeToString(time);
if (!items[strTime]) {
items[strTime] = [];
const numItems = 3;
for (let j = 0; j < numItems; j++) {
items[strTime].push({
name: 'Item for ' + '#' + j,
height: Math.max(50, Math.floor(Math.random() * 150)),
day: strTime + j,
});
}
}
}
const newItems: AgendaSchedule = {};
Object.keys(items).forEach(key => {
newItems[key] = items[key];
});
this.setState({
items: newItems,
});
}, 1000);
};
updateItem = (id: string) => {
const newItems: AgendaSchedule = {...this.state.items};
if (this.state.items) {
Object.keys(this.state.items).forEach(key => {
newItems[key] = this.updateAgendaEntry(key, id);
});
this.setState({
items: newItems,
});
}
};
updateAgendaEntry = (date: string, id: string) => {
let agendaEntryToUpdate: AgendaEntry[] = Object.assign([], this.state.items?.[date]);
let index = agendaEntryToUpdate.findIndex(a => a.day === id);
agendaEntryToUpdate[index] = Object.assign(
{},
{
...agendaEntryToUpdate[index],
name: 'Has changed !',
}
);
return agendaEntryToUpdate;
};
renderItem = (reservation: AgendaEntry, isFirst: boolean) => {
const fontSize = isFirst ? 16 : 14;
const color = isFirst ? 'black' : '#43515c';
return (
<TouchableOpacity style={[styles.item, {height: reservation.height}]} onPress={() => this.updateItem(reservation.day)}>
<Text style={{fontSize, color}}>{reservation.name}</Text>
</TouchableOpacity>
);
};
renderEmptyDate = () => {
return (
<View style={styles.emptyDate}>
<Text>This is empty date!</Text>
</View>
);
};
rowHasChanged = (r1: AgendaEntry, r2: AgendaEntry) => {
return r1.name !== r2.name;
};
timeToString(time: number) {
const date = new Date(time);
return date.toISOString().split('T')[0];
}
}
const styles = StyleSheet.create({
item: {
backgroundColor: 'white',
flex: 1,
borderRadius: 5,
padding: 10,
marginRight: 10,
marginTop: 17,
},
emptyDate: {
height: 15,
flex: 1,
paddingTop: 30,
},
});
About this issue
- Original URL
- State: open
- Created 2 years ago
- Reactions: 6
- Comments: 19
Commits related to this issue
- hard codded the dash title name after completion The problem seems to be with respect to the imported agenda component. https://github.com/wix/react-native-calendars/issues/1855 — committed to Delocy/StudyPals by Delocy a year ago
- update the calendar component Hard coded the complete function so that the dash would appear after reloading the task. But still unsure of how to do that with the edit function. I suspect the agenda ... — committed to Delocy/StudyPals by Delocy a year ago
I seem to have a similar problem here.
Agenda
does not rerender whenitems
changes.Same as #1589
Any updates on the issue? Patched the library for now, but it would be nice to see this fixed.
You should use patch-package to patch this. Easier to maintain your changes by keeping your changes when you remove your node module folder and auto merge as much as possible when you update react-native-calendars