ngrx-loading-state
Version:
NgRx Loading State consistently manages loading actions such as API fetches.
151 lines (150 loc) • 7.72 kB
TypeScript
import { DefaultProjectorFn, MemoizedSelector } from '@ngrx/store';
import { Action, TypedAction } from '@ngrx/store/src/models';
import { catchError } from 'rxjs';
import { ActionFactoryResult, FailureAction, LoadAction, LoadingActionsReducerTypes, LoadingState, LoadingStates, OnState } from './loading-state-types';
/**
* This class bundles up a set of load, success, and failure actions. It contains helpers to create
* reducers for these actions and helpers for creating selectors.
*
* Do not use this class directly, use the createLoadingActions() helper function.
*
*/
export declare class LoadingActions<LoadPayloadType extends object, SuccessPayloadType extends object, FailurePayloadType extends object> {
/** The actions the user can dispatch */
readonly load: ActionFactoryResult<LoadAction & LoadPayloadType>;
readonly success: ActionFactoryResult<SuccessPayloadType>;
readonly failure: ActionFactoryResult<FailureAction & FailurePayloadType>;
constructor(options: {
load: ActionFactoryResult<LoadAction & LoadPayloadType>;
success: ActionFactoryResult<SuccessPayloadType>;
failure: ActionFactoryResult<FailureAction & FailurePayloadType>;
});
/**
* Type guard to test if action is of type LoadingActions.load.
*
* @param action Any ngrx action
* @returns True if action if of type LoadingActions.load
*/
instanceOfLoad(action: Action): action is ReturnType<ActionFactoryResult<LoadAction & LoadPayloadType>>;
/**
* Type guard to test if action is of type LoadingActions.success.
*
* @param action Any ngrx action
* @returns True if action if of type LoadingActions.success
*/
instanceOfSuccess(action: Action): action is ReturnType<ActionFactoryResult<SuccessPayloadType>>;
/**
* Type guard to test if action is of type LoadingActions.failure.
*
* @param action Any ngrx action
* @returns True if action if of type LoadingActions.failure
*/
instanceOfFailure(action: Action): action is ReturnType<ActionFactoryResult<FailureAction & FailurePayloadType>>;
/**
* Creates reducers for the load, success, failure actions.
*
* @param options.onLoad Call back when action is LoadAction. Return new copy of state if state needs to change.
* @param options.onSuccess Call back when action is SuccessAction. Return new copy of state if state needs to change.
* @param options.onFailure Call back when action is FailureAction. Return new copy of state if state needs to change.
* @returns A tuple of "on()" instances that handles load, success, failure actions in this order.
* @example
* export const reducer = createReducer(
* initialState,
* // Note: (1)
* ...fetchItem.reducer<ItemState>({
* onSuccess: (state, { item }): ItemState => {
* return { ...state, item };
* },
* // You can also customise what happens for LoadAction and FailureAction through onLoad and onFailure.
* // But most of the time, there's nothing to do for those. They are automatically handled by the fetchItem.reducer
* }),
* );
*
* (1) We need this explicit return type because "return type widening" means that the
* type constraints on the onSuccess function is only narrowing, so if you provide more
* fields in the return object that is not in VaultState, there is no error. Because
* the return value can be narrowed to the onSuccess function's constraints. So we have
* to explicitly specify the return type here. This is a well known issue that has been
* raise since at least 2016. So does not look like it will be fixed. Also, it's a recommended
* pattern in ngrx reducers to have this explicit typing.
*
* Ref https://github.com/microsoft/TypeScript/issues/241#issuecomment-327269994
*
* (2): Given that there are explicity return types on the onLoad, onSuccess, onFailure functions
* it should be possible to infer the type of the state here. But can't figure out how.
*
*/
reducer<State extends {
loadingStates: LoadingStates;
}>(options?: {
onLoad?: (state: OnState<State>, action: LoadAction & LoadPayloadType & TypedAction<string>) => State;
onSuccess?: (state: OnState<State>, action: SuccessPayloadType & TypedAction<string>) => State;
onFailure?: (state: OnState<State>, action: FailureAction & FailurePayloadType & TypedAction<string>) => State;
}): [
LoadingActionsReducerTypes<State>,
LoadingActionsReducerTypes<State>,
LoadingActionsReducerTypes<State>
];
/**
* Catches errors in effect.
*
* @returns rxjs operator that emits a FailureAction.
* @example
*
* fetchCount$ = createEffect(() => {
* return this.actions$.pipe(
* ofType(fetchCount.load),
* filterLoading(this.store.select(fetchCountSelectors.state)),
* switchMap((action) => {
* return apiCAll().pipe(
* map(item => fetchCount.success(item)),
* fetchCount.catchError()
* );
* })
* );
* });
*
*/
catchError(): ReturnType<typeof catchError>;
/**
* Returns a map of selectors for loading, success, error, and the entire loading state. Similar to the ngrx entities adaptor.
*
* Design: The advantage of doing it in a bundle is that we can share the result of createStateSelector().
* If we had separate individual functions, each function might need to call createStateSelector()
* to create a new instance of the selector. We can't cache any created selectors because that will cause
* a memory leak since the cached references are always held in this class and hence does not get released.
*
* @param selectLoadingStates Selector that returns the loadingStats of the feature slice. You can use createLoadingStatesSelector()
* to create it.
* @returns A collection of selectors
* state: the LoadingState
* loading: True if loading
* success: True if last load was successful
* error: any errors from the last API call.
* @example
* // The feature slice selector. Standard ngrx stuff.
* const selectState = createFeatureSelector<SimpleState>(SIMPLE_FEATURE_KEY);
*
* // Selector that selects the loadingStates field from the global store. The createLoadingStatesSelector()
* // is provided as a part of this lib as well.
* const selectLoadingStates = createLoadingStatesSelector(selectState);
*
* // Create the selectors related to the fetchItem loading state.
* export const fetchItemSelectors = fetchItem.createSelectors(selectLoadingStates);
*
* // You can then observe the loading states:
* this.store.select(fetchItemSelectors.state); // The entire LoadingState
* this.store.select(fetchItemSelectors.success); // The boolean success flag
* this.store.select(fetchItemSelectors.loading); // The boolean loading flag
*
*/
createSelectors(selectLoadingStates: MemoizedSelector<object, LoadingStates, DefaultProjectorFn<LoadingStates>>): {
state: MemoizedSelector<object, LoadingState, DefaultProjectorFn<LoadingState>>;
loading: MemoizedSelector<object, boolean, DefaultProjectorFn<boolean>>;
success: MemoizedSelector<object, boolean, DefaultProjectorFn<boolean>>;
error: MemoizedSelector<object, any, DefaultProjectorFn<any>>;
};
private get key();
private getLoadingState;
private setState;
}