synapse-storage
Version:
Набор инструментов для управления состоянием и апи-запросами
57 lines (56 loc) • 4.01 kB
TypeScript
import type { IStorage } from '../storage';
import type { ISelectorModule, SelectorAPI, SelectorOptions } from './selector.interface';
/**
* Публичный class-based слой селекторов. Селекторы объявляются как поля класса через
* фабрики `this.select` / `this.combine` / `this.keyed` — поля сразу настоящие
* `SelectorAPI` (eager-материализация, никаких рецептов).
*
* Внешние селекторы (cross-store) передаются параметрами конструктора подкласса:
* parameter properties присваиваются ДО инициализаторов полей, поэтому `this.core`
* в полях доступен корректно.
*
* Внутреннее состояние базы — hard-private (`#`-поля/методы): их имена в отдельном
* namespace и НЕ конфликтуют с полями-селекторами подкласса (можно объявить селектор
* `track`, `module` и т.п.). Зарезервированы лишь `protected`/публичные члены
* (`select`/`combine`/`keyed`/`destroy`) — их именами селекторы называть нельзя.
*
* @example
* ```ts
* class PostsSelectors extends Selectors<PostsState> {
* constructor(storage: IStorage<PostsState>, private readonly core: CoreSelectors) {
* super(storage)
* }
*
* private readonly api = this.select((s) => s.api)
* readonly list = this.select((s) => s.list)
* readonly isPostsLoading = this.combine([this.api], (a) => a.postsRequest.status === 'loading')
* // cross-store: пересчитывается при изменении чужого стора
* readonly currentUserId = this.combine([this.core.profile], (p) => p?.user_info?.id ?? null)
* }
* ```
*/
export declare abstract class Selectors<TState extends Record<string, any>> {
#private;
/** Принимает `storage` (создаёт и владеет своим `SelectorModule`) либо готовый модуль. */
constructor(source: IStorage<TState> | ISelectorModule<TState>);
/** Простой селектор: мемоизация по ссылке стейта + трекинг затронутых ключей. */
protected select<R>(selector: (state: TState) => R, options?: SelectorOptions<R>): SelectorAPI<R>;
/** Combined-селектор; зависимости — любые `SelectorAPI`, в т.ч. из других сторов. */
protected combine<Deps extends unknown[], R>(deps: {
[K in keyof Deps]: SelectorAPI<Deps[K]>;
}, fn: (...args: Deps) => R, options?: SelectorOptions<R>): SelectorAPI<R>;
/**
* Параметрический (keyed) селектор: один `SelectorAPI` на ключ (кэш по ключу).
*
* Слайсы соседних ключей живут под общим родителем (`s.byTarget[key]`), а storage при
* обновлении пере-клонирует всю ветку — поэтому ссылка соседнего ключа не сохраняется.
* Чтобы обновление ключа A не уведомляло подписчиков ключа B, keyed-селекторы по
* умолчанию сравнивают значения структурно (`deepEquals`). Опции можно переопределить.
*/
protected keyed<K extends string | number, R>(fn: (key: K) => (state: TState) => R, options?: SelectorOptions<R>): (key: K) => SelectorAPI<R>;
/**
* Уничтожает свой модуль (только если владеет им). Если модуль передан снаружи —
* удаляет из него лишь свои селекторы, чужие не трогает.
*/
destroy(): void;
}