Отслеживание query
router.trackQuery связывает query-параметры URL с событиями модели. Это нужно, когда часть интерфейса хранится в строке запроса: диалог, фильтр, вкладка, сортировка или состояние встроенного виджета.
router.query подходит для прямого чтения. router.trackQuery нужен, когда вход или выход из query-состояния должен запускать код модели.
API трекера
interface QueryTracker<Parameters> {
entered: Event<Parameters>;
exited: Event<void>;
enter: EventCallable<Parameters>;
exit: EventCallable<{ ignoreParams: string[] } | void>;
}parameters принимает любой объект с safeParse:
interface QuerySchema<T> {
safeParse(query: Query):
| { success: true; data: T }
| { success: false };
}Роутер не требует Zod. Можно использовать Zod, Valibot, свой парсер или маленькую inline-схему.
Диалог в query
Частый сценарий — диалог, который открывается через query:
const inviteDialog = router.trackQuery({
forRoutes: [teamRoute],
parameters: {
safeParse(query) {
return query.dialog === "invite"
? { success: true, data: { dialog: "invite" as const } }
: { success: false };
},
},
});Открыть и закрыть диалог можно через tracker:
inviteDialog.enter({ dialog: "invite" });
inviteDialog.exit();entered срабатывает, когда query подходит под схему и один из forRoutes открыт. exited срабатывает, когда query больше не подходит или приложение уходит с этих роутов.
Фильтры
Фильтры удобно читать из query, чтобы перезагрузка страницы сохраняла состояние:
const issueFilter = router.trackQuery({
forRoutes: [issuesRoute],
parameters: {
safeParse(query) {
const status = query.status;
return status === "open" || status === "closed"
? { success: true, data: { status } }
: { success: false };
},
},
});
reaction({
on: issueFilter.entered,
run({ status }) {
issues.status.value = status;
void issues.loadFx();
},
});Записать фильтр в URL:
issueFilter.enter({ status: "open" });Закрыть и оставить часть query
По умолчанию exit() очищает query:
inviteDialog.exit();Если закрытие одного состояния не должно стереть другое, передается список параметров, которые надо оставить:
inviteDialog.exit({ ignoreParams: ["tab", "sort"] });Ручная проверка
По умолчанию tracker пересчитывается при изменении роута или query. Поле check используется, если проверка должна запускаться только по отдельному событию:
const refreshed = event<void>();
const preview = router.trackQuery({
check: refreshed,
parameters: previewSchema,
});Это полезно, если парсер дорогой или host-страница меняет query часто, а модель не должна реагировать на каждое изменение.