From 315be28d42e6e0201c34cd2327c31277167868c1 Mon Sep 17 00:00:00 2001 From: Petyo Ivanov Date: Sat, 29 Apr 2023 21:24:52 +0300 Subject: [PATCH] fix: avoid restoreStateFrom overwriting data when props change --- examples/chat.tsx | 18 +++++++++++++++++- src/stateLoadSystem.ts | 15 ++++++++++----- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/examples/chat.tsx b/examples/chat.tsx index 9a666328d..48bcfc70c 100644 --- a/examples/chat.tsx +++ b/examples/chat.tsx @@ -20,13 +20,15 @@ function generateMessages(length: number): Message[] { const initialChannelData = Array.from({ length: 3 }, (_, index) => { return { id: index, + firstItemIndex: 200000, name: `Channel ${index}`, - messages: generateMessages(130), + messages: generateMessages(20), } }) initialChannelData.push({ id: 3, + firstItemIndex: 200000, name: 'Channel 3', messages: generateMessages(1), }) @@ -52,6 +54,18 @@ export function Example() { [currentChannelId, channels] ) + const loadOlderMessages = React.useCallback(() => { + setTimeout(() => { + setChannels((channels) => { + return produce(channels, (draft) => { + const channel = draft.find((x) => x.id === currentChannelId)! + channel.messages.unshift(...generateMessages(20)) + channel.firstItemIndex -= 20 + }) + }) + }, 1000) + }, [currentChannelId, channels]) + const selectChannel = React.useCallback( (id: number) => { if (currentChannelId !== null) { @@ -114,6 +128,8 @@ export function Example() { followOutput={followOutput} itemContent={virtosoItemContent} data={channel.messages} + firstItemIndex={channel.firstItemIndex} + startReached={loadOlderMessages} {...(channelState ? {} : { initialTopMostItemIndex: channel.messages.length - 1 })} /> diff --git a/src/stateLoadSystem.ts b/src/stateLoadSystem.ts index 7b956f233..ec96aadad 100644 --- a/src/stateLoadSystem.ts +++ b/src/stateLoadSystem.ts @@ -1,10 +1,11 @@ import { domIOSystem } from './domIOSystem' import { initialTopMostItemIndexSystem } from './initialTopMostItemIndexSystem' import { StateSnapshot, StateCallback } from './interfaces' +import { propsReadySystem } from './propsReadySystem' import { sizeSystem, sizeTreeToRanges } from './sizeSystem' import * as u from './urx' -export const stateLoadSystem = u.system(([{ sizes, sizeRanges }, { scrollTop }, { initialTopMostItemIndex }]) => { +export const stateLoadSystem = u.system(([{ sizes, sizeRanges }, { scrollTop }, { initialTopMostItemIndex }, { didMount }]) => { const getState = u.stream() const restoreStateFrom = u.statefulStream(undefined) @@ -17,9 +18,13 @@ export const stateLoadSystem = u.system(([{ sizes, sizeRanges }, { scrollTop }, u.connect( u.pipe( - restoreStateFrom, - u.filter(u.isDefined), - u.map((snapshot) => snapshot!.ranges) + didMount, + u.withLatestFrom(restoreStateFrom), + u.filter(([, state]) => state !== undefined), + u.distinctUntilChanged(), + u.map(([, snapshot]) => { + return snapshot!.ranges + }) ), sizeRanges ) @@ -28,7 +33,7 @@ export const stateLoadSystem = u.system(([{ sizes, sizeRanges }, { scrollTop }, getState, restoreStateFrom, } -}, u.tup(sizeSystem, domIOSystem, initialTopMostItemIndexSystem)) +}, u.tup(sizeSystem, domIOSystem, initialTopMostItemIndexSystem, propsReadySystem)) function locationFromSnapshot(snapshot: StateSnapshot | undefined) { return { offset: snapshot!.scrollTop, index: 0, align: 'start' }