@rx-angular/state
Version:
@rx-angular/state is a light-weight, flexible, strongly typed and tested tool dedicated to reduce the complexity of managing component state and side effects in angular
504 lines • 21.2 kB
TypeScript
import { Signal } from '@angular/core';
import { AccumulationFn, KeyCompareMap, PickSlice } from '@rx-angular/state/selections';
import { Observable, OperatorFunction, Subscribable, Unsubscribable } from 'rxjs';
import { SignalStateProxy } from './signal-state-proxy';
import * as i0 from "@angular/core";
export type ProjectStateFn<Type> = (oldState: Type) => Partial<Type>;
export type ProjectValueFn<Type, Key extends keyof Type> = (oldState: Type) => Type[Key];
export type ProjectStateReducer<Type, Value> = (oldState: Type, value: Value) => Partial<Type>;
export type ProjectValueReducer<Type, Key extends keyof Type, Value> = (oldState: Type, value: Value) => Type[Key];
export type ReadOnly = 'get' | 'select' | 'computed' | 'signal';
/**
* @description
* RxState is a light-weight reactive state management service for managing local state in angular.
*
* @example
* Component({
* selector: 'app-stateful',
* template: `<div>{{ state$ | async | json }}</div>`,
* providers: [RxState]
* })
* export class StatefulComponent {
* readonly state$ = this.state.select();
*
* constructor(private state: RxState<{ foo: string }>) {}
* }
*
* @docsCategory RxState
* @docsPage RxState
*/
export declare class RxState<State extends object> implements Subscribable<State> {
private subscription;
protected scheduler: import("rxjs").SchedulerLike | "sync" | null;
private accumulator;
private effectObservable;
private readonly injector;
private signalStoreProxy;
/**
* @description
* The unmodified state exposed as `Observable<State>`. It is not shared, distinct or gets replayed.
* Use the `$` property if you want to read the state without having applied {@link stateful} to it.
*/
readonly $: Observable<State>;
/**
* @internal
*/
constructor();
/**
* @description
*
* Return RxState in ReadOnly mode exposing only methods for reading state
* get(), select(), computed() and signal() methods.
* This can be helpful when you don't want others to write in your state.
*
* @example
* ```typescript
* const readOnlyState = state.asReadOnly();
* const getNum = state.get('num');
* const selectNum$ = state.select('num');
* ```
*
* @return Pick<RxState<State>, ReadOnly>
*/
asReadOnly(): Pick<RxState<State>, ReadOnly>;
/**
* @description
*
* Allows to customize state accumulation function.
* This can be helpful to implement deep updates and tackle other immutability problems in a custom way.
* @example
*
* ```typescript
* const myAccumulator = (state: MyState, slice: Partial<MyState>) => deepCopy(state, slice);
*
* this.state.setAccumulator(myAccumulator);
* ```
*
* @param {AccumulationFn} accumulatorFn
* @return void
*
* @deprecated
* Use `provideRxStateConfig` and provide the accumulator with the `withAccumulator` provider function.
* Will be removed in future versions.
*/
setAccumulator(accumulatorFn: AccumulationFn): void;
/**
* @description
* Read from the state in imperative manner. Returns the state object in its current state.
*
* @example
* const { disabled } = state.get();
* if (!disabled) {
* doStuff();
* }
*
* @return State
*/
get(): State;
/**
* @description
* Read from the state in imperative manner by providing keys as parameters.
* Returns the part of state object.
*
* @example
* // Access a single property
* const bar = state.get('bar');
*
* // Access a nested property
* const foo = state.get('bar', 'foo');
*
* @param {KeyA} keyA
* @return State[KeyA]
*/
get<KeyA extends keyof State>(keyA: KeyA): State[KeyA];
/** @internal **/
get<KeyA extends keyof State, KeyB extends keyof State[KeyA]>(keyA: KeyA, keyB: KeyB): State[KeyA][KeyB];
/** @internal **/
get<KeyA extends keyof State, KeyB extends keyof State[KeyA], KeyC extends keyof State[KeyA][KeyB]>(keyA: KeyA, keyB: KeyB, keyC: KeyC): State[KeyA][KeyB][KeyC];
/** @internal **/
get<KeyA extends keyof State, KeyB extends keyof State[KeyA], KeyC extends keyof State[KeyA][KeyB], KeyD extends keyof State[KeyA][KeyB][KeyC]>(keyA: KeyA, keyB: KeyB, keyC: KeyC, keyD: KeyD): State[KeyA][KeyB][KeyC][KeyD];
/** @internal **/
get<KeyA extends keyof State, KeyB extends keyof State[KeyA], KeyC extends keyof State[KeyA][KeyB], KeyD extends keyof State[KeyA][KeyB][KeyC], KeyE extends keyof State[KeyA][KeyB][KeyC][KeyD]>(keyA: KeyA, keyB: KeyB, keyC: KeyC, keyD: KeyD, keyE: KeyE): State[KeyA][KeyB][KeyC][KeyD][KeyE];
/** @internal **/
get<KeyA extends keyof State, KeyB extends keyof State[KeyA], KeyC extends keyof State[KeyA][KeyB], KeyD extends keyof State[KeyA][KeyB][KeyC], KeyE extends keyof State[KeyA][KeyB][KeyC][KeyD], KeyF extends keyof State[KeyA][KeyB][KeyC][KeyD][KeyE]>(keyA: KeyA, keyB: KeyB, keyC: KeyC, keyD: KeyD, keyE: KeyE, keyF: KeyF): State[KeyA][KeyB][KeyC][KeyD][KeyE][KeyF];
/**
* @description
* Manipulate one or many properties of the state by providing
* a `Partial<State>`state or a `ProjectionFunction<State>`.
*
* @example
* // Update one or many properties of the state by providing a `Partial<State>`
*
* const partialState = {
* foo: 'bar',
* bar: 5
* };
* state.set(partialState);
*
* // Update one or many properties of the state by providing a `ProjectionFunction<State>`
*
* const reduceFn = oldState => ({
* bar: oldState.bar + 5
* });
* state.set(reduceFn);
*
* @param {Partial<State>|ProjectStateFn<State>} stateOrProjectState
* @return void
*/
set(stateOrProjectState: Partial<State> | ProjectStateFn<State>): void;
/**
* @description
* Manipulate a single property of the state by the property name and a `ProjectionFunction<State>`.
*
* @example
* const reduceFn = oldState => oldState.bar + 5;
* state.set('bar', reduceFn);
*
* @param {Key} key
* @param {ProjectValueFn<State, Key>} projectSlice
* @return void
*/
set<Key extends keyof State, Object>(key: Key, projectSlice: ProjectValueFn<State, Key>): void;
/**
* @description
* Connect an `Observable<Partial<State>>` to the state `State`.
* Any change emitted by the source will get merged into the state.
* Subscription handling is done automatically.
*
* @example
* const sliceToAdd$ = interval(250).pipe(mapTo({
* bar: 5,
* foo: 'foo'
* });
* state.connect(sliceToAdd$);
* // every 250ms the properties bar and foo get updated due to the emission of sliceToAdd$
*
* // Additionally you can provide a `projectionFunction` to access the current state object and do custom mappings.
*
* const sliceToAdd$ = interval(250).pipe(mapTo({
* bar: 5,
* foo: 'foo'
* });
* state.connect(sliceToAdd$, (state, slice) => state.bar += slice.bar);
* // every 250ms the properties bar and foo get updated due to the emission of sliceToAdd$. Bar will increase by
* // 5 due to the projectionFunction
*
* @param {Observable<Partial<State>>} inputOrSlice$
* @return void
*/
connect(inputOrSlice$: Observable<Partial<State>>): void;
/**
* @description
* Connect a `Signal<Partial<State>>` to the state `State`.
* Any change emitted by the source will get merged into the state.
*
* @example
* const partialState = signal({ foo: 'foo', bar: 5 });
* state.connect(partialState);
*
* @param {Signal<Partial<State>>} signal
* @return void
*/
connect(signal: Signal<Partial<State>>): void;
/**
* @description
* Connect an `Observable<Value>` to the state `State`.
* Any change emitted by the source will get forwarded to project function and merged into the state.
* Subscription handling is done automatically.
*
* You have to provide a `projectionFunction` to access the current state object and do custom mappings.
*
* @example
* const sliceToAdd$ = interval(250);
* state.connect(sliceToAdd$, (type, value) => ({bar: value}));
* // every 250ms the property bar get updated due to the emission of sliceToAdd$
*
* @param {Observable<Value>} inputOrSlice$
* @param {ProjectStateReducer<State, Value>} projectFn
* @return void
*/
connect<Value>(inputOrSlice$: Observable<Value>, projectFn: ProjectStateReducer<State, Value>): void;
/**
* @description
* Connect a `Signal<Value>` to the state `State`.
* Any change emitted by the source will get forwarded to the project function and merged into the state.
*
* You have to provide a `projectionFunction` to access the current state object and do custom mappings.
*
* @example
* const signalSlice = signal(5);
* state.connect(signalSlice, (type, value) => ({bar: value}));
*
* @param {Signal<Value>} signal
* @param {ProjectStateReducer<State, Value>} projectFn
* @return void
*/
connect<Value>(signal: Signal<Value>, projectFn: ProjectStateReducer<State, Value>): void;
/**
*
* @description
* Connect an `Observable<State[Key]>` source to a specific property `Key` in the state `State`.
* Any emitted change will update this specific property in the state.
* Subscription handling is done automatically.
*
* @example
* const myTimer$ = interval(250);
* state.connect('timer', myTimer$);
* // every 250ms the property timer will get updated
* @param {Key} key
* @param {Observable<State[Key]>} slice$
*
* @return void
*/
connect<Key extends keyof State>(key: Key, slice$: Observable<State[Key]>): void;
/**
*
* @description
* Connect a `Signal<State[Key]>` source to a specific property `Key` in the state `State`.
* Any emitted change will update this specific property in the state.
*
* @example
* const currentTime = signal(Date.now())
* state.connect('currentTime', currentTime);
*
* @param {Key} key
* @param {Signal<State[Key]>} signal
*
* @return void
*/
connect<Key extends keyof State>(key: Key, signal: Signal<State[Key]>): void;
/**
* @description
* Connect an `Observable<Value>` source to a specific property in the state. Additionally, you can provide a
* `projectionFunction` to access the current state object on every emission of your connected `Observable`.
* Any change emitted by the source will get merged into the state.
* Subscription handling is done automatically.
*
* @example
* const myTimer$ = interval(250);
* state.connect('timer', myTimer$, (state, timerChange) => state.timer += timerChange);
* // every 250ms the property timer will get updated
*
* @param {Key} key
* @param {Observable<Value>} input$
* @param {ProjectValueReducer<State, Key, Value>} projectSliceFn
*
* @return void
*/
connect<Key extends keyof State, Value>(key: Key, input$: Observable<Value>, projectSliceFn: ProjectValueReducer<State, Key, Value>): void;
/**
*
* @description
* Connect a `Signal<Value>` source to a specific property in the state. Additionally, you can provide a
* `projectionFunction` to access the current state object on every emission of your connected `Observable`.
* Any change emitted by the source will get merged into the state.
* Subscription handling is done automatically.
*
* @example
* const currentTime = signal(Date.now())
* state.connect('currentTime', currentTime, (state, currentTime) => state.currentTime = currentTime);
*
* @param {Key} key
* @param {Signal<Value>} signal
* @param {ProjectValueReducer<State, Key, Value>} projectSliceFn
*
* @return void
*/
connect<Key extends keyof State, Value>(key: Key, signal: Signal<Value>, projectSliceFn: ProjectValueReducer<State, Key, Value>): void;
/**
* @description
* Returns the state as cached and distinct `Observable<Type>`.
* This way you don't have to think about
* **late subscribers**, **multiple subscribers** or **multiple emissions** of the same value
*
* @example
* const state$ = state.select();
* state$.subscribe(state => doStuff(state));
*
* @returns Observable<TType>
*/
select(): Observable<State>;
/**
* @description
* Returns the state as cached and distinct `Observable<TypeA>`. Accepts arbitrary
* [rxjs operators](https://rxjs-dev.firebaseapp.com/guide/operators)
* to enrich the selection with reactive composition.
*
* @example
* const profilePicture$ = state.select(
* map((state) => state.profilePicture),
* switchMap(profilePicture => mapImageAsync(profilePicture))
* );
* @param op { OperatorFunction<Type, TypeA> }
* @returns Observable<TypeA>
*/
select<TypeA = State>(op: OperatorFunction<State, TypeA>): Observable<TypeA>;
/**
* @internal
*/
select<TypeA = State, TypeB = TypeA>(op1: OperatorFunction<State, TypeA>, op2: OperatorFunction<TypeA, TypeB>): Observable<TypeB>;
/**
* @internal
*/
select<TypeA = State, TypeB = TypeA, TypeC = TypeB>(op1: OperatorFunction<State, TypeA>, op2: OperatorFunction<TypeA, TypeB>, op3: OperatorFunction<TypeB, TypeC>): Observable<TypeC>;
/**
* @internal
*/
select<TypeA = State, TypeB = TypeA, TypeC = TypeB, TypeD = TypeC>(op1: OperatorFunction<State, TypeA>, op2: OperatorFunction<TypeA, TypeB>, op3: OperatorFunction<TypeB, TypeC>, op4: OperatorFunction<TypeC, TypeD>): Observable<TypeD>;
/**
* @internal
*/
select<TypeA = State, TypeB = TypeA, TypeC = TypeB, TypeD = TypeC, TypeE = TypeD>(op1: OperatorFunction<State, TypeA>, op2: OperatorFunction<TypeA, TypeB>, op3: OperatorFunction<TypeB, TypeC>, op4: OperatorFunction<TypeC, TypeD>, op5: OperatorFunction<TypeD, TypeE>): Observable<TypeE>;
/**
* @description
* Transform a slice of the state by providing keys and map function.
* Returns result of applying function to state slice as cached and distinct `Observable<Value>`.
*
* @example
* // Project state slice
* const text$ = state.select(
* ['query', 'results'],
* ({ query, results }) => `${results.length} results found for "${query}"`
* );
*
* @param {Key[]} keys
* @param {(slice: PickSlice<Type, Key>) => Value} fn
* @param {KeyCompareMap<Pick<Type, Key>>} keyCompareMap
*
* @return Observable<Value>
*/
select<Key extends keyof State, Value>(keys: Key[], fn?: (slice: PickSlice<State, Key>) => Value, keyCompareMap?: KeyCompareMap<Pick<State, Key>>): Observable<Value>;
/**
* @description
* Transform a single property of the state by providing a key and map function.
* Returns result of applying function to state property as cached and distinct `Observable<Value>`.
*
* @example
* // Project state based on single property
* const foo$ = state.select('bar', bar => `bar equals ${bar}`);
*
* @param {Key} key
* @param {(val: Type[Key]) => Value} fn
*
* @return Observable<Value>
*/
select<Key extends keyof State, Value>(key: Key, fn: (val: State[Key]) => Value): Observable<Value>;
/**
* @description
* Access a single property of the state by providing keys.
* Returns a single property of the state as cached and distinct `Observable<State[KeyA]>`.
*
* @example
* // Access a single property
*
* const bar$ = state.select('bar');
*
* // Access a nested property
*
* const foo$ = state.select('bar', 'foo');
*
* @return Observable<Type[KeyA]>
*/
select<KeyA extends keyof State>(keyA: KeyA): Observable<State[KeyA]>;
/**
* @internal
*/
select<KeyA extends keyof State, KeyB extends keyof State[KeyA]>(keyA: KeyA, keyB: KeyB): Observable<State[KeyA][KeyB]>;
/**
* @internal
*/
select<KeyA extends keyof State, KeyB extends keyof State[KeyA], KeyC extends keyof State[KeyA][KeyB]>(keyA: KeyA, keyB: KeyB, keyC: KeyC): Observable<State[KeyA][KeyB][KeyC]>;
/**
* @internal
*/
select<KeyA extends keyof State, KeyB extends keyof State[KeyA], KeyC extends keyof State[KeyA][KeyB], KeyD extends keyof State[KeyA][KeyB][KeyC]>(keyA: KeyA, keyB: KeyB, keyC: KeyC, keyD: KeyD): Observable<State[KeyA][KeyB][KeyC][KeyD]>;
/**
* @internal
*/
select<KeyA extends keyof State, KeyB extends keyof State[KeyA], KeyC extends keyof State[KeyA][KeyB], KeyD extends keyof State[KeyA][KeyB][KeyC], KeyE extends keyof State[KeyA][KeyB][KeyC][KeyD]>(keyA: KeyA, keyB: KeyB, keyC: KeyC, keyD: KeyD, keyE: KeyE): Observable<State[KeyA][KeyB][KeyC][KeyD][KeyE]>;
/**
* @internal
*/
select<KeyA extends keyof State, KeyB extends keyof State[KeyA], KeyC extends keyof State[KeyA][KeyB], KeyD extends keyof State[KeyA][KeyB][KeyC], KeyE extends keyof State[KeyA][KeyB][KeyC][KeyD], KeyF extends keyof State[KeyA][KeyB][KeyC][KeyD][KeyE]>(keyA: KeyA, keyB: KeyB, keyC: KeyC, keyD: KeyD, keyE: KeyE, keyF: KeyF): Observable<State[KeyA][KeyB][KeyC][KeyD][KeyE][KeyF]>;
/**
* @description
* Returns a signal of the given key. It's first value is determined by the
* current keys value in RxState. Whenever the key gets updated, the signal
* will also be updated accordingly.
*
* @example
* const fooSignal = state.signal('foo');
*
* @param {Key} key
*
* @return Signal<State[Key]>
*/
signal<Key extends keyof State>(key: Key): Signal<State[Key]>;
/**
* @description
* Lets you create a computed signal based off multiple keys stored in RxState.
*
* @example
* const computedSignal = state.computed((s) => s.foo + s.bar);
*
* @param {(slice: SignalStateProxy<Type>) => ComputedType} fn
* @return Signal<ComputedType>
*/
computed<ComputedType>(fn: (slice: SignalStateProxy<State>) => ComputedType): Signal<ComputedType>;
/**
* @description
* Lets you create a computed signal derived from state and rxjs operators.
*
* @throws If the initial value is not provided and the signal is not sync.
* Use startWith() to provide an initial value.
*
* @example
* const computedSignal = state.computedFrom(
* map(state => state.foo),
* filter(foo => foo > 5)
* );
*
* @param op1 { OperatorFunction<Type, TypeA> }
* @returns Signal<TypeA>
*/
computedFrom<TypeA = State>(op1: OperatorFunction<State, TypeA>): Signal<TypeA>;
/** @internal */
computedFrom<TypeA = State, TypeB = TypeA>(op1: OperatorFunction<State, TypeA>, op2: OperatorFunction<TypeA, TypeB>): Signal<TypeB>;
/** @internal */
computedFrom<TypeA = State, TypeB = TypeA, TypeC = TypeB>(op1: OperatorFunction<State, TypeA>, op2: OperatorFunction<TypeA, TypeB>, op3: OperatorFunction<TypeB, TypeC>): Signal<TypeC>;
/** @internal */
computedFrom<TypeA = State, TypeB = TypeA, TypeC = TypeB, TypeD = TypeC>(op1: OperatorFunction<State, TypeA>, op2: OperatorFunction<TypeA, TypeB>, op3: OperatorFunction<TypeB, TypeC>, op4: OperatorFunction<TypeC, TypeD>): Signal<TypeD>;
/** @internal */
computedFrom<TypeA = State, TypeB = TypeA, TypeC = TypeB, TypeD = TypeC, TypeE = TypeD>(op1: OperatorFunction<State, TypeA>, op2: OperatorFunction<TypeA, TypeB>, op3: OperatorFunction<TypeB, TypeC>, op4: OperatorFunction<TypeC, TypeD>, op5: OperatorFunction<TypeD, TypeE>): Signal<TypeE>;
/**
* @description
* Manages side-effects of your state. Provide an `Observable<any>`
* **side-effect** and an optional `sideEffectFunction`.
* Subscription handling is done automatically.
*
* @example
* // Directly pass an observable side-effect
* const localStorageEffect$ = changes$.pipe(
* tap(changes => storeChanges(changes))
* );
* state.hold(localStorageEffect$);
*
* // Pass an additional `sideEffectFunction`
*
* const localStorageEffectFn = changes => storeChanges(changes);
* state.hold(changes$, localStorageEffectFn);
*
* @param {Observable<SideEffect>} obsOrObsWithSideEffect
* @param {function} [sideEffectFn]
*
* @return void
*/
hold<SideEffect>(obsOrObsWithSideEffect: Observable<SideEffect>, sideEffectFn?: (arg: SideEffect) => void): void;
/**
* @internal
*/
subscribe(): Unsubscribable;
static ɵfac: i0.ɵɵFactoryDeclaration<RxState<any>, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<RxState<any>>;
}
//# sourceMappingURL=rx-state.service.d.ts.map