от src избавился
This commit is contained in:
@@ -62,6 +62,12 @@ export function AsyncSearchableSelect<T>({
|
||||
// Растущий счётчик запросов: если ответ пришёл не от последнего запроса
|
||||
// (например, пользователь допечатал ещё символ) — просто игнорируем его.
|
||||
const requestIdRef = React.useRef(0);
|
||||
// getValue нужен внутри runSearch (дедуп при доклейке страницы), но в deps его
|
||||
// класть нельзя: пропс приходит инлайновой стрелкой и меняет ссылку на каждом
|
||||
// рендере — runSearch пересоздавался бы постоянно и перезапускал эффект
|
||||
// бесконечной прокрутки. Держим последнюю версию в ref.
|
||||
const getValueRef = React.useRef(getValue);
|
||||
getValueRef.current = getValue;
|
||||
|
||||
const runSearch = React.useCallback((q: string, targetPage: number, append: boolean) => {
|
||||
const requestId = ++requestIdRef.current;
|
||||
@@ -71,7 +77,16 @@ export function AsyncSearchableSelect<T>({
|
||||
search(q, targetPage)
|
||||
.then(result => {
|
||||
if (requestIdRef.current !== requestId) return; // устарел
|
||||
setItems(prev => (append ? [...prev, ...result.items] : result.items));
|
||||
// Дедуп при доклейке: соседние страницы могут пересекаться, если индекс
|
||||
// переупорядочился между запросами (StartRow смещает окно по РАНГУ, а не
|
||||
// по устойчивому курсору). Дубликат здесь = два пункта с одинаковым
|
||||
// React-ключом, то есть заявка в списке дважды. Внутри одной страницы
|
||||
// дубли отсекает уже источник (см. searchRequests в requestManageService).
|
||||
setItems(prev => {
|
||||
if (!append) return result.items;
|
||||
const shown = new Set(prev.map(i => getValueRef.current(i)));
|
||||
return [...prev, ...result.items.filter(i => !shown.has(getValueRef.current(i)))];
|
||||
});
|
||||
setHasMore(result.hasMore);
|
||||
setPage(targetPage);
|
||||
})
|
||||
|
||||
@@ -17,7 +17,7 @@ import {
|
||||
} from '../models';
|
||||
|
||||
// Тонкий 1:1 фасад над контроллерами бэкенда:
|
||||
// backend/src/Tnsu.ApprovalRoute.Api/Controllers/{RequestTypes,TemplateRoutes,TemplateRouteSteps}Controller.cs
|
||||
// backend/Tnsu.ApprovalRoute.Api/Controllers/{RequestTypes,TemplateRoutes,TemplateRouteSteps}Controller.cs
|
||||
|
||||
export class ApprovalRouteApi {
|
||||
constructor(private readonly http: IHttpClient) {}
|
||||
|
||||
@@ -152,7 +152,22 @@ export class RequestManageService {
|
||||
});
|
||||
|
||||
const rawRows = (result.PrimarySearchResults ?? []) as Array<{ ListItemID?: string }>;
|
||||
const ids = rawRows.map(r => Number(r.ListItemID)).filter(id => !isNaN(id) && id > 0);
|
||||
// Дедуп по Id обязателен: TrimDuplicates: false выше выключает штатную
|
||||
// дедупликацию Search (она иногда схлопывает нужные нам заявки), и тогда
|
||||
// индекс может отдать один и тот же элемент несколькими хитами
|
||||
// (переиндексация, несколько «версий» в crawl). Без этого фильтра хиты
|
||||
// превращались в пункты комбобокса один-в-один, и заявка показывалась в
|
||||
// списке дважды — с одинаковым React-ключом. Та же причина и то же лечение,
|
||||
// что в request-portal (features/myRequests/myRequests.service.ts/hitsToEntries).
|
||||
const seen = new Set<number>();
|
||||
const ids = rawRows
|
||||
.map(r => Number(r.ListItemID))
|
||||
.filter(id => !isNaN(id) && id > 0)
|
||||
.filter(id => {
|
||||
if (seen.has(id)) return false;
|
||||
seen.add(id);
|
||||
return true;
|
||||
});
|
||||
if (ids.length === 0) return { items: [], hasMore: false };
|
||||
|
||||
const list = sp.web.getList(requestType.listId);
|
||||
|
||||
Reference in New Issue
Block a user