Перейти к содержанию

Управление состоянием

Обзор

Приложение разделяет состояние на два уровня:

  • Серверное состояние --- данные, полученные с сервера (списки документов, заданий, реестров, групп, настройки). Управляется библиотекой TanStack React Query.
  • Местное состояние --- данные, существующие только в браузере (состояние формы, раскрытие панели, выбранный элемент). Управляется встроенными средствами React (useState, useReducer).

Серверное состояние: React Query

Принцип работы

React Query автоматически:

  • сохраняет ответы сервера и повторно использует их, пока данные не устареют;
  • повторяет запрос при возникновении ошибки;
  • обновляет данные в фоновом режиме, когда они считаются устаревшими;
  • отменяет запрос, если компонент, запросивший данные, был удалён со страницы.

Настройка

Клиент запросов создаётся в App.tsx со следующими параметрами:

  • Время устаревания --- одна минута. После получения ответа данные считаются свежими в течение минуты; при повторном обращении в этот период запрос к серверу не выполняется.
  • Повторный запрос при возврате к окну --- отключён. Данные не обновляются автоматически, когда пользователь переключается обратно на вкладку приложения.

Получение данных: useQuery

Используется на всех страницах для получения списков и отдельных записей:

const { data: health, isLoading } = useQuery({
  queryKey: ['health'],
  queryFn: () => healthApi.check(),
  refetchInterval: 30000,
})

Параметр queryKey определяет ключ сохранения. Если два компонента запрашивают данные с одинаковым ключом, запрос к серверу выполняется только один раз, а результат используется обоими.

Некоторые запросы настроены на периодическое обновление через refetchInterval --- например, проверка состояния системы каждые 30 секунд или отслеживание прогресса обработки.

Постраничная загрузка: useInfiniteQuery

Используется для загрузки длинных списков по частям --- например, при фильтрации значений колонки в таблице результатов реестра:

const { data, fetchNextPage, hasNextPage } = useInfiniteQuery({
  queryKey: ['column-values', registryId, fieldId, search],
  queryFn: ({ pageParam = 0 }) =>
    registriesApi.getColumnValues(registryId, { skip: pageParam }),
  getNextPageParam: (lastPage) => /* следующая порция */,
  initialPageParam: 0,
})

При прокрутке списка вниз вызывается fetchNextPage, и следующая порция данных добавляется к уже загруженным.

Изменение данных: useMutation

Используется для отправки изменений на сервер (создание, обновление, удаление):

const confirmMutation = useMutation({
  mutationFn: (groupId: string) => groupsApi.confirm(groupId),
  onSuccess: () => {
    queryClient.invalidateQueries({ queryKey: ['groups'] })
  },
})

После успешного выполнения действия вызывается invalidateQueries, чтобы React Query повторно запросил актуальные данные с сервера. Это обеспечивает автоматическое обновление списка после подтверждения группы, удаления документа или изменения настроек.

Ключи сохранения

Каждый тип данных использует свой ключ:

Ключ Данные
['health'] состояние системы
['jobs'], ['jobs', 'recent'] задания обработки
['documents'] список документов
['groups'] группы документов
['registries'] список реестров
['registry', id] конкретный реестр
['registry-rows', id] строки реестра
['column-values', id, field] значения колонки для фильтрации
['settings'] настройки приложения
['archive'] дерево файлового архива

Составные ключи (например, ['jobs', 'recent']) позволяют точечно обновлять хранилище: можно обновить только последние задания, не затрагивая полный список.

Местное состояние

Встроенные средства React

Для состояния, которое существует только внутри одного компонента или страницы, используются стандартные перехватчики React:

  • useState --- хранение простых значений: состояние раскрытия панели, текущий шаг мастера, содержимое поля ввода, выбранный элемент списка.
  • useReducer --- хранение сложного состояния с несколькими связанными полями (используется в перехватчике useToast для управления списком уведомлений).

Состояние в адресной строке

Часть состояния хранится в параметрах адресной строки через перехватчик useModalRouter. Это позволяет:

  • сохранять открытые окна при обновлении страницы;
  • использовать кнопки «Назад» и «Вперёд» для навигации между окнами;
  • копировать ссылку на конкретный документ или исходный файл.

Подробнее --- в разделе маршрутизация.

Постоянное хранение в браузере

Единственное значение, сохраняемое в localStorage, --- состояние сворачивания бокового меню. При следующем визите боковое меню остаётся в том же положении, в котором его оставил пользователь.

Поток данных

Сервер (FastAPI)
    ↑↓ HTTP-запросы (Axios)
Прослойка обращений (api/client.ts)
    ↑↓ вызовы функций
React Query (хранилище в памяти браузера)
    ↑↓ перехватчики useQuery / useMutation
Компоненты страниц (pages/*.tsx)
    ↑↓ свойства и обратные вызовы
Базовые компоненты (components/ui/*.tsx)

Данные всегда проходят через React Query, даже если нужен единственный запрос. Это обеспечивает единообразное поведение: индикатор загрузки, обработка ошибок, автоматическое обновление.