NEXT_PAGE joins ANY fetch in flight: a read-ahead during an invalidation or a poll joined that refetch and appended no page, and nothing asked again until the reader moved the range (the channel page during a backfill). The read-ahead now waits while the list fetches anything (mayReadAhead, re-run on isFetching); a Load older click cancels a poll instead (nextPageOnClick). A page turn to a revealed row restarted the progressive mount at 30 rows; Channels focuses that row in the next commit, and at index 40 of a 50-row page it was not there, so focus fell to <body>. The mount now reaches through the revealed row.
26 lines
1.7 KiB
TypeScript
26 lines
1.7 KiB
TypeScript
/**
|
||
* What every `fetchNextPage` call passes (lint-enforced). By default react-query CANCELS a next-page
|
||
* fetch that is still in flight and sends it again with the same cursor. Our requests do not take the
|
||
* abort signal, so the first one is not stopped: the server ran both. The feed's read-ahead effect
|
||
* re-runs on every scroll frame, and a frame that lands before `isFetchingNextPage` turns true calls
|
||
* again. On prod (E31 S4e) every feed page went out 2–3 times; on a 0.8 s title sort the copies
|
||
* queued to 2.3 s. With this a second call joins the fetch already running.
|
||
*
|
||
* The join takes ANY fetch in flight, not only a next page: a call during a background refetch (an
|
||
* invalidation, a poll) joins THAT, which reloads the pages already there and appends none (E31 S5).
|
||
* So a read-ahead also waits while the list is fetching at all (`mayReadAhead`) and runs again when
|
||
* the fetch settles; a click uses `nextPageOnClick`.
|
||
*/
|
||
export const NEXT_PAGE = { cancelRefetch: false } as const;
|
||
|
||
/** Whether an infinite list's read-ahead may ask for the next page now. Not while ANY fetch of the
|
||
* list is in flight: that call would join it (see NEXT_PAGE). The caller re-runs on `isFetching`. */
|
||
export const mayReadAhead = (hasNextPage: boolean, isFetching: boolean) =>
|
||
hasNextPage && !isFetching;
|
||
|
||
/** The options for a next page a person asked for (a "Load older" button): join the page already
|
||
* loading, else cancel a background refetch and load the page — a click must not be swallowed by a
|
||
* poll that happened to be running. */
|
||
export const nextPageOnClick = (isFetchingNextPage: boolean) =>
|
||
isFetchingNextPage ? NEXT_PAGE : ({ cancelRefetch: true } as const);
|