Ensure EventListSummary key does not change during backpagination (#7915)
This commit is contained in:
parent
115e17b097
commit
482d756bd0
2 changed files with 105 additions and 19 deletions
|
@ -254,6 +254,9 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
private readonly showTypingNotificationsWatcherRef: string;
|
||||
private eventTiles: Record<string, EventTile> = {};
|
||||
|
||||
// A map to allow groupers to maintain consistent keys even if their first event is uprooted due to back-pagination.
|
||||
public grouperKeyMap = new WeakMap<MatrixEvent, string>();
|
||||
|
||||
constructor(props, context) {
|
||||
super(props, context);
|
||||
|
||||
|
@ -684,14 +687,13 @@ export default class MessagePanel extends React.Component<IProps, IState> {
|
|||
break; // break on first grouper
|
||||
}
|
||||
}
|
||||
|
||||
if (!grouper) {
|
||||
const wantTile = this.shouldShowEvent(mxEv);
|
||||
const isGrouped = false;
|
||||
if (wantTile) {
|
||||
if (this.shouldShowEvent(mxEv)) {
|
||||
// make sure we unpack the array returned by getTilesForEvent,
|
||||
// otherwise react will auto-generate keys and we will end up
|
||||
// replacing all of the DOM elements every time we paginate.
|
||||
ret.push(...this.getTilesForEvent(prevEvent, mxEv, last, isGrouped, nextEvent, nextTile));
|
||||
// otherwise React will auto-generate keys, and we will end up
|
||||
// replacing all the DOM elements every time we paginate.
|
||||
ret.push(...this.getTilesForEvent(prevEvent, mxEv, last, false, nextEvent, nextTile));
|
||||
prevEvent = mxEv;
|
||||
}
|
||||
|
||||
|
@ -1127,7 +1129,7 @@ class CreationGrouper extends BaseGrouper {
|
|||
if (!this.events || !this.events.length) return [];
|
||||
|
||||
const panel = this.panel;
|
||||
const ret = [];
|
||||
const ret: ReactNode[] = [];
|
||||
const isGrouped = true;
|
||||
const createEvent = this.event;
|
||||
const lastShownEvent = this.lastShownEvent;
|
||||
|
@ -1135,7 +1137,7 @@ class CreationGrouper extends BaseGrouper {
|
|||
if (panel.wantsDateSeparator(this.prevEvent, createEvent.getDate())) {
|
||||
const ts = createEvent.getTs();
|
||||
ret.push(
|
||||
<li key={ts+'~'}><DateSeparator key={ts+'~'} roomId={createEvent.getRoomId()} ts={ts} /></li>,
|
||||
<li key={ts+'~'}><DateSeparator roomId={createEvent.getRoomId()} ts={ts} /></li>,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1262,6 +1264,10 @@ class MainGrouper extends BaseGrouper {
|
|||
this.events.push(ev);
|
||||
}
|
||||
|
||||
private generateKey(): string {
|
||||
return "eventlistsummary-" + this.events[0].getId();
|
||||
}
|
||||
|
||||
public getTiles(): ReactNode[] {
|
||||
// If we don't have any events to group, don't even try to group them. The logic
|
||||
// below assumes that we have a group of events to deal with, but we might not if
|
||||
|
@ -1271,24 +1277,28 @@ class MainGrouper extends BaseGrouper {
|
|||
const isGrouped = true;
|
||||
const panel = this.panel;
|
||||
const lastShownEvent = this.lastShownEvent;
|
||||
const ret = [];
|
||||
const ret: ReactNode[] = [];
|
||||
|
||||
if (panel.wantsDateSeparator(this.prevEvent, this.events[0].getDate())) {
|
||||
const ts = this.events[0].getTs();
|
||||
ret.push(
|
||||
<li key={ts+'~'}><DateSeparator key={ts+'~'} roomId={this.events[0].getRoomId()} ts={ts} /></li>,
|
||||
<li key={ts+'~'}><DateSeparator roomId={this.events[0].getRoomId()} ts={ts} /></li>,
|
||||
);
|
||||
}
|
||||
|
||||
// Ensure that the key of the EventListSummary does not change with new events.
|
||||
// This will prevent it from being re-created unnecessarily, and
|
||||
// instead will allow new props to be provided. In turn, the shouldComponentUpdate
|
||||
// method on ELS can be used to prevent unnecessary renderings.
|
||||
//
|
||||
// Whilst back-paginating with an ELS at the top of the panel, prevEvent will be null,
|
||||
// so use the key "eventlistsummary-initial". Otherwise, use the ID of the first
|
||||
// membership event, which will not change during forward pagination.
|
||||
const key = "eventlistsummary-" + (this.prevEvent ? this.events[0].getId() : "initial");
|
||||
// Ensure that the key of the EventListSummary does not change with new events in either direction.
|
||||
// This will prevent it from being re-created unnecessarily, and instead will allow new props to be provided.
|
||||
// In turn, the shouldComponentUpdate method on ELS can be used to prevent unnecessary renderings.
|
||||
const keyEvent = this.events.find(e => this.panel.grouperKeyMap.get(e));
|
||||
const key = keyEvent ? this.panel.grouperKeyMap.get(keyEvent) : this.generateKey();
|
||||
if (!keyEvent) {
|
||||
// Populate the weak map with the key that we are using for all related events.
|
||||
this.events.forEach(e => {
|
||||
if (!this.panel.grouperKeyMap.has(e)) {
|
||||
this.panel.grouperKeyMap.set(e, key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let highlightInSummary = false;
|
||||
let eventTiles = this.events.map((e, i) => {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue