Add manual per-page memoization to post select (#2146)

zio/stable
dan 2023-12-08 21:57:00 +00:00 committed by GitHub
parent 61fa3d506c
commit 7b686b5592
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 122 additions and 59 deletions

View File

@ -1,4 +1,4 @@
import {useCallback, useEffect} from 'react' import React, {useCallback, useEffect} from 'react'
import { import {
AppBskyFeedDefs, AppBskyFeedDefs,
AppBskyFeedPost, AppBskyFeedPost,
@ -97,6 +97,22 @@ export function usePostFeedQuery(
const feedTuners = useFeedTuners(feedDesc) const feedTuners = useFeedTuners(feedDesc)
const moderationOpts = useModerationOpts() const moderationOpts = useModerationOpts()
const enabled = opts?.enabled !== false && Boolean(moderationOpts) const enabled = opts?.enabled !== false && Boolean(moderationOpts)
const lastRun = React.useRef<{
data: InfiniteData<FeedPageUnselected>
args: typeof selectArgs
result: InfiniteData<FeedPage>
} | null>(null)
// Make sure this doesn't invalidate unless really needed.
const selectArgs = React.useMemo(
() => ({
feedTuners,
disableTuner: params?.disableTuner,
moderationOpts,
ignoreFilterFor: opts?.ignoreFilterFor,
}),
[feedTuners, params?.disableTuner, moderationOpts, opts?.ignoreFilterFor],
)
const query = useInfiniteQuery< const query = useInfiniteQuery<
FeedPageUnselected, FeedPageUnselected,
@ -147,12 +163,53 @@ export function usePostFeedQuery(
: undefined, : undefined,
select: useCallback( select: useCallback(
(data: InfiniteData<FeedPageUnselected, RQPageParam>) => { (data: InfiniteData<FeedPageUnselected, RQPageParam>) => {
const tuner = params?.disableTuner // If the selection depends on some data, that data should
// be included in the selectArgs object and read here.
const {feedTuners, disableTuner, moderationOpts, ignoreFilterFor} =
selectArgs
const tuner = disableTuner
? new NoopFeedTuner() ? new NoopFeedTuner()
: new FeedTuner(feedTuners) : new FeedTuner(feedTuners)
return {
// Keep track of the last run and whether we can reuse
// some already selected pages from there.
let reusedPages = []
if (lastRun.current) {
const {
data: lastData,
args: lastArgs,
result: lastResult,
} = lastRun.current
let canReuse = true
for (let key in selectArgs) {
if (selectArgs.hasOwnProperty(key)) {
if ((selectArgs as any)[key] !== (lastArgs as any)[key]) {
// Can't do reuse anything if any input has changed.
canReuse = false
break
}
}
}
if (canReuse) {
for (let i = 0; i < data.pages.length; i++) {
if (data.pages[i] && lastData.pages[i] === data.pages[i]) {
reusedPages.push(lastResult.pages[i])
// Keep the tuner in sync so that the end result is deterministic.
tuner.tune(lastData.pages[i].feed)
continue
}
// Stop as soon as pages stop matching up.
break
}
}
}
const result = {
pageParams: data.pageParams, pageParams: data.pageParams,
pages: data.pages.map(page => ({ pages: [
...reusedPages,
...data.pages.slice(reusedPages.length).map(page => ({
api: page.api, api: page.api,
tuner, tuner,
cursor: page.cursor, cursor: page.cursor,
@ -167,7 +224,7 @@ export function usePostFeedQuery(
for (let i = 0; i < slice.items.length; i++) { for (let i = 0; i < slice.items.length; i++) {
if ( if (
moderations[i]?.content.filter && moderations[i]?.content.filter &&
slice.items[i].post.author.did !== opts?.ignoreFilterFor slice.items[i].post.author.did !== ignoreFilterFor
) { ) {
return undefined return undefined
} }
@ -180,13 +237,15 @@ export function usePostFeedQuery(
slice.items.length > 1 && slice.items.length > 1 &&
slice.items.every( slice.items.every(
item => item =>
item.post.author.did === slice.items[0].post.author.did, item.post.author.did ===
slice.items[0].post.author.did,
), ),
items: slice.items items: slice.items
.map((item, i) => { .map((item, i) => {
if ( if (
AppBskyFeedPost.isRecord(item.post.record) && AppBskyFeedPost.isRecord(item.post.record) &&
AppBskyFeedPost.validateRecord(item.post.record).success AppBskyFeedPost.validateRecord(item.post.record)
.success
) { ) {
return { return {
_reactKey: `${slice._reactKey}-${i}`, _reactKey: `${slice._reactKey}-${i}`,
@ -207,9 +266,13 @@ export function usePostFeedQuery(
}) })
.filter(Boolean) as FeedPostSlice[], .filter(Boolean) as FeedPostSlice[],
})), })),
],
} }
// Save for memoization.
lastRun.current = {data, result, args: selectArgs}
return result
}, },
[feedTuners, params?.disableTuner, moderationOpts, opts?.ignoreFilterFor], [selectArgs /* Don't change. Everything needs to go into selectArgs. */],
), ),
}) })