Search requests (#2)
Co-authored-by: gorbenko.pavel <gorbenko.pavel@tansu.kz> Reviewed-on: http://git.tnsu.kz/timur.gorodetskiy/request-portal/pulls/2 Co-authored-by: timur.gorodetskiy <timur.gorodetskiy@tnsu.kz> Co-committed-by: timur.gorodetskiy <timur.gorodetskiy@tnsu.kz>
This commit was merged in pull request #2.
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
import * as React from 'react';
|
||||
|
||||
// Текст поиска переживает перезагрузку страницы (localStorage), но не более
|
||||
// того — это просто «продолжить с того же места», а не синхронизация между
|
||||
// вкладками/устройствами. key должен быть уникальным на раздел (см. вызовы в
|
||||
// RequestsTableView/SearchView), иначе значения разных таблиц перепишут друг друга.
|
||||
function readStored(storageKey: string): string {
|
||||
try {
|
||||
return window.localStorage.getItem(storageKey) || '';
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
export function usePersistedQuery(key: string): [string, (value: string) => void] {
|
||||
const storageKey = `rp-search-query:${key}`;
|
||||
|
||||
const [value, setValue] = React.useState<string>(() => readStored(storageKey));
|
||||
|
||||
// Компонент может продолжать жить, когда key поменялся: RequestsTableView —
|
||||
// это один и тот же элемент для вкладок «На согласовании» и «Мои заявки»,
|
||||
// меняется только проп kind. Инициализатор useState при этом повторно НЕ
|
||||
// срабатывает, поэтому без перечитывания текст из одной вкладки оставался бы
|
||||
// на экране во второй и записывался уже под её ключ — выглядело бы так, будто
|
||||
// ключ у них общий.
|
||||
const prevKey = React.useRef<string>(storageKey);
|
||||
React.useEffect(() => {
|
||||
if (prevKey.current === storageKey) return;
|
||||
prevKey.current = storageKey;
|
||||
setValue(readStored(storageKey));
|
||||
}, [storageKey]);
|
||||
|
||||
const setPersisted = React.useCallback(
|
||||
(next: string): void => {
|
||||
setValue(next);
|
||||
try {
|
||||
if (next) {
|
||||
window.localStorage.setItem(storageKey, next);
|
||||
} else {
|
||||
window.localStorage.removeItem(storageKey);
|
||||
}
|
||||
} catch {
|
||||
// localStorage недоступен (приватный режим и т.п.) — просто не сохраняем.
|
||||
}
|
||||
},
|
||||
[storageKey]
|
||||
);
|
||||
|
||||
return [value, setPersisted];
|
||||
}
|
||||
|
||||
// Задержка перед тем, как введённый текст уйдёт в запрос. Без неё поиск
|
||||
// срабатывает на КАЖДОЕ нажатие клавиши: «согласование» — это 12 полных циклов
|
||||
// (Search + дочитывание через REST по каждому типу заявок), из которых нужен
|
||||
// только последний. Заодно снимает часть нагрузки, из-за которой SharePoint
|
||||
// начинает throttling'ить (429).
|
||||
const SEARCH_DEBOUNCE_MS = 450;
|
||||
|
||||
// Значение, отстающее от переданного на SEARCH_DEBOUNCE_MS: в поле ввода текст
|
||||
// меняется сразу (без «залипания»), а в запрос уходит только когда пользователь
|
||||
// перестал печатать. Пустая строка проходит без задержки — очистка поиска
|
||||
// должна возвращать полный список мгновенно.
|
||||
export function useDebouncedQuery(value: string, delayMs: number = SEARCH_DEBOUNCE_MS): string {
|
||||
const [debounced, setDebounced] = React.useState<string>(value);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!value) {
|
||||
setDebounced('');
|
||||
return undefined;
|
||||
}
|
||||
const timer = window.setTimeout(() => setDebounced(value), delayMs);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, [value, delayMs]);
|
||||
|
||||
return debounced;
|
||||
}
|
||||
Reference in New Issue
Block a user