UNPKG

synapse-storage

Version:

Набор инструментов для управления состоянием и апи-запросами

57 lines (56 loc) 4.01 kB
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; }