npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

ngrx-loading-state

v1.0.13

Published

NgRx Loading State consistently manages loading actions such as API fetches.

Readme

NgRx Loading State

NgRx Loading State consistently manages loading actions such as API fetches.

Features

Reduce boiler plate and improve consistency

The load, success, failure actions almost always go together. The reducers and selectors that deal with them are also boilerplates. User NgRx Loading State to have them automatically created. But more importantly, have them handle consistently across the application.\

Avoid redundant API calls

NgRx Loading State decides if an API call needs to be made on a per action basis, using a maxAge concept around the timestamp of the last successful API call. With NgRx Loading State, you can issue loading actions in smart component wherever the data is needed, without explicitly needing to check if data already exists in the store.

Sits on top of NgRx

All functions are helpers that delegates to NgRx components, so completely conforms to NGRX idioms. This means you can easily use this library for you new loading actions without making any changes to existing actions.

Loading state is associated with actions

It is often necessary to load multiple pieces of data into the store with a single action for the sake of having a consistent collection of data (i.e. a single update of the state in the reducer). Therefore, the best place to track loading states is per action.

Installation

npm install ngrx-loading-state

Demo

Repository here: https://github.com/amzhang/ngrx-loading-state-demo

Live demo here.

Setup

For completeness, the complete code, including all imports, boilerplate for setting up ngrx etc are included below.

Actions

// simple.actions.ts

import { createLoadingActions, failure, load, success } from 'ngrx-loading-state';

export const fetchUser = createLoadingActions(
  'Fetch User',
  load<{ userId: string }>(),
  success<{ user: object }>(),
  failure<{}>()
);

Reducer

// simple.reducer.ts

import { createReducer } from '@ngrx/store';
import { getInitialState, WithLoadingStates } from 'ngrx-loading-state';
import { fetchUser } from './simple.actions';

export const SIMPLE_FEATURE_KEY = 'simple';

export type SimpleState = WithLoadingStates & {
  user?: object;
};

export const initialState: SimpleState = {
  ...getInitialState()
};

export const simpleReducer = createReducer(
  initialState,

  // fetchUser.reducer() returns an array of reducers to handle [load, success, failure] actions.
  // Usually on the success action need to be customised in the reducer, as is done here via the onSuccess()
  // callback.
  ...fetchUser.reducer<SimpleState>({
    onSuccess: (state, { user }): SimpleState => {
      return {
        ...state,
        user,
      };
    },
  }),
);

Selectors

import { createFeatureSelector, createSelector } from '@ngrx/store';
import { createLoadingStatesSelector } from 'ngrx-loading-state';
import { fetchUser } from './simple.actions';
import { SimpleState } from './simple.reducer';

// Boilerplate, only needed once per feature slice.
const selectState = createFeatureSelector<SimpleState>(SIMPLE_FEATURE_KEY);
const selectLoadingStates = createLoadingStatesSelector(selectState);

export const fetchUserSelectors = fetchUser.createSelectors(selectLoadingStates);

Effects

import { Injectable } from '@angular/core';
import { Actions, createEffect, ofType } from '@ngrx/effects';
import { Store } from '@ngrx/store';
import { filterLoading } from 'ngrx-loading-state';
import { map, switchMap } from 'rxjs/operators';
import { fetchUser } from './simple.actions';
import { fetchUserSelectors } from './simple.selectors';

@Injectable()
export class SimpleEffects {
  constructor(private actions$: Actions, private store: Store) {}

  fetchCount$ = createEffect(() => {
    return this.actions$.pipe(
      ofType(fetchUser.load),

      // This filters out redundant API calls
      filterLoading(this.store.select(fetchUserSelectors.state)),

      switchMap((action) => {
        return of(true).pipe(
          delay(1000), // simulate loading.
          map(() => {
            return fetchUser.success({
              user: {},
            });
          }),
          // Errors will be updated into the loading state in the store for this action.
          fetchUser.catchError()
        );
      })
    );
  });

Usage

You can issue loading actions anywhere, but usually they are issued in the smart component that needs the data. For example, a typical component might look like this:

@Component({
  selector: 'loading-state-demo',
  templateUrl: './loading-state-demo.component.html',
  styleUrls: ['./loading-state-demo.component.scss'],
})
export class LoadingStateDemoComponent {
  // The loading state contains these fields:
  //   loading: boolean - is API in progress?
  //   success: boolean - did last API call succeed? False if API call in progress
  //   successTimestamp?: boolean - Unix time of last successful API call
  //   error?: boolean - error from latest API call, undefined curren API call in progress or no error from last API call.
  fetchUserState$ = this.store.select(fetchUserSelectors.state);
 
  constructor(private store: Store) {
    this.simpleFacade.fetchUser({ userId: '123' });
  }
}

and in the html template you can react to the loading state:

<div>{{ (fetchUserState$ | async)?.success ? 'Data has loaded' : 'Data has not loaded yet'}}</div>

By default, loading action always issue a new API call:

this.simpleFacade.fetchUser({ userId: '123' });
  • If you don't want to issue an new API call if one is already in progress, then use:
this.simpleFacade.fetchUser({ userId: '123', maxAge: MAX_AGE_LATEST });
  • If you don't want to issue a new API call if the last successful API call was less than 5 seconds ago, then use:
this.simpleFacade.fetchUser({ userId: '123', maxAge: 5000 });
  • If you don't want to issue a new API call as long as data has been successfully loaded previously:
this.simpleFacade.fetchUser({ userId: '123', maxAge: Infinity });