diff --git a/web/src/pages/Svod2Page/useSvod2Pages.js b/web/src/pages/Svod2Page/useSvod2Pages.js index af0a00e..2c02353 100644 --- a/web/src/pages/Svod2Page/useSvod2Pages.js +++ b/web/src/pages/Svod2Page/useSvod2Pages.js @@ -17,6 +17,7 @@ import { getVisiblePage, getWindowPages, getWindowRange, + shouldFetchFirstPageFallback, shouldRefine, svod2PagingReducer, } from './svod2Paging'; @@ -59,7 +60,7 @@ export function useSvod2Pages({ sheet, year, orgIds, queryKey, search, columnFil if (mountedRef.current) setState(next); }, []); - const fetchPage = useCallback(async (ctx, pageIndex) => { + const fetchPage = useCallback(async (ctx, pageIndex, signal = ctx.controller.signal) => { ctx.inflight.add(pageIndex); try { const response = await SummaryApi.getSvod2Page( @@ -71,7 +72,7 @@ export function useSvod2Pages({ sheet, year, orgIds, queryKey, search, columnFil columnFilters: ctx.columnFilters, offset: pageIndex * PAGE_SIZE, }), - { signal: ctx.controller.signal }, + { signal }, ); const rows = response.result || []; return { rows, total: response.total ?? rows.length }; @@ -138,14 +139,43 @@ export function useSvod2Pages({ sheet, year, orgIds, queryKey, search, columnFil const loadPages = async () => { try { - const first = await fetchPage(ctx, candidate); + // Страница по прокрутке и (на глубине) запасная offset=0 летят параллельно: если страница по прокрутке + // за концом нового списка, сервер отвечает на неё долго и без строк, а страница 0 уже даёт total. + const candidateController = new AbortController(); + const zeroController = new AbortController(); + const abortRequests = () => { + candidateController.abort(); + zeroController.abort(); + }; + ctx.controller.signal.addEventListener('abort', abortRequests); + const settle = (request, index) => + request.then( + (result) => ({ result, index }), + (error) => ({ error, index }), + ); + const candidateRequest = settle(fetchPage(ctx, candidate, candidateController.signal), candidate); + const zeroRequest = candidate > 0 ? settle(fetchPage(ctx, 0, zeroController.signal), 0) : null; + let answer = zeroRequest ? await Promise.race([candidateRequest, zeroRequest]) : await candidateRequest; + if ( + answer.index === 0 && + candidate > 0 && + !answer.error && + shouldFetchFirstPageFallback({ candidate, total: answer.result.total }) + ) { + candidateController.abort(); + } else { + zeroController.abort(); + answer = await candidateRequest; + if (answer.error) throw answer.error; + } if (ctx.controller.signal.aborted) return; + const first = answer.result; const { total } = first; const pages = getWindowFor(total); const pageCount = getPageCount(total); let cache = new Map(); - if (total === 0 || candidate < pageCount) { - const pageIndex = total === 0 ? 0 : candidate; + if (total === 0 || answer.index < pageCount) { + const pageIndex = total === 0 ? 0 : answer.index; dispatch({ type: 'page', sheet: sheetKey, generation, pageIndex, rows: first.rows, total, keepPages: pages }); cache = stateRef.current[sheetKey].pages; }