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

@zeit-dev/ngrx-util

v2.0.0-pre.1

Published

Utility classes, functions we commonly use across ngrx projects.

Readme

@zeit-dev/ngrx-util

size npm license github codeclimate coverage

What is it?

This is a tiny (but growing) collection of small, composable utilities to make working with NgRx easier.

Goals:

  • composable - easy to extend by combining with own/other tools
  • functional - prefer (pure) functions over thick services
  • lightweight and treeshake-able
  • comprehensible - less of a blackbox

Compatibility

  • Angular 8.x, 9.x
  • NgRx 8.x, 9.x (with compatible Angular version of course)

Installation

npm add @zeit-dev/ngrx-util

or

yarn add @zeit-dev/ngrx-util

Usage

As this is a library of mainly functions and types, there is no Angular module to import.

Loading async data into state

This library provides a set of functions and types to assist in the recurring requirement of loading async data (typically from a REST API) into the state.

1. Define State-model

To represent the (loaded/loading/failed) data within the state, we wrap it in a ResourceState wrapper type:

/**
 * Simple wrapper for loaded data in the state.
 */
export interface ResourceState<TResource, TParams = void> {
  /**
   * Has this resource been loaded?
   * `true` *after* the success action
   */
  loaded: boolean;

  /**
   * Is this resource currently loading?
   * `false` *before* any load action,
   * `true` *after* the load action,
   * `false` *after* success or fail
   */
  loading: boolean;

  /**
   * The stores the loaded data.
   * If no data has been loaded yet, contains the initial value (see {@link initial}).
   */
  results: TResource;

  /**
   * If data has been loaded, and was loaded with params,
   * stores these params of the last request.
   */
  lastParams: TParams | undefined;

  /**
   * If data is currently loading, and loading was requested with params,
   * stores these params.
   */
  loadingParams: TParams | undefined;
}

TResource is the type of your data, it could be anything. Typically an object (single resource) or a list.
Note: results is intentionally not nullable (or undefined)! This allows you to specify the initial/default value yourself (e.g. [] for list data). TParams is the type of parameters you may need to load the data. More on that later.

There are 3 helper functions to make the task of loading the data (effect) and storing that (and the meta information in the wrapper) in the state (reducer): createLoadActions, createLoadReducer, createLoadEffect.

So let's define a state shape, and an initial state content:

import {ResourceState, initial} from '@zeit-dev/ngrx-util'

export interface Todo {
  title: string;
}

const TodoState = ResourceState<Todo[]>;
const initialTodoState = initial<Todo[]>([]);

2. createLoadActions - define actions

In order to load an external resource, we want 3 actions/events to represent the lifecycle:

  • load: Initiates the async request/task. Optionally takes a params object.
  • success: Indicates the successful return of the async task.
  • failed: Indicates a failure in the async task.
import { createLoadActions } from "@zeit-dev/ngrx-util";

const todoActions = createLoadActions("todo");
// => { load: LoadActionCreator, success: SuccessActionCreator, failed: FailedActionCreator }

Note: If you don't want an actions-object {load, success,failed}, just destructure it right away:

import { createLoadActions } from "@zeit-dev/ngrx-util";

const {
  load: loadtodo,
  success: todoSuccess,
  failed: todoFailed,
} = createLoadActions("todo");

3. createLoadReducer - get it into state

This function does not define a complete reducer, instead (to be composable) it returns 3 Ons, see NgRx Reducers.

So you would typically do:

import { createLoadReducer } from "@zeit-dev/ngrx-util";
import { ActionReducerMap, createReducer } from "@ngrx/store";

export const reducers: ActionReducerMap<{ todos: TodoState }> = {
  todos: createReducer(
    initialTodoState,
    ...createLoadReducer<Todo[], TodoState>(todoActions),
  ),
};

This creates 3 reductions:

  1. on load: Sets loading: true (and updates loadingParams from load.params)
  2. on success: Sets loaded: true and loading: false, updates results with the loaded data (see effects below)
  3. on failed: Sets loading: false

4. createLoadEffect - do the actual work

As with createLoadReducer this does not define a complete effect, but provides a composable part to incorporate into your own effect.
It basically just does 3 things:

  1. filter actions (on type load)
  2. hand load.params over to a user defined callback to do the actual loading
  3. take the return value of the callback, wrap it in success (and let NgRx dispatch that)

Additionally to that, it can propagate errors to Angulars ErrorHandler (if you supply it), it dispatches failed on errors and it passes the current state (or substate if you use this._store$.select(...) in to the callback)

import { HttpClient } from "@angular/common/http";
import { ErrorHandler, Injectable } from "@angular/core";
import { Actions, createEffect } from "@ngrx/effects";
import { Store } from "@ngrx/store";
import { createLoadEffect } from "@zeit-dev/ngrx-util";
import { delay } from "rxjs/operators";
import { AppStateSlice, todoActions } from "./reducers";

export const TODOS_URL = "https://jsonplaceholder.typicode.com/todos";

@Injectable()
export class AppEffects {
  readonly loadTodos$ = createEffect(() =>
    this._actions$.pipe(
      createLoadEffect(
        todoActions,
        (params, state) => {
          /*
        If you want idempotency, add sth like
        if (todoState.loaded && isEqual(todoState.lastParams, params)) return of(todoState.results);
        or debouncing
        if (todoState.loading && isEqual(todoState.loadingParams, params)) return of(todoState.results);
      */
          return this._http.get(TODOS_URL).pipe(
            // Simulate some loading time
            delay(2000),
          );
        },
        this._store$,
        this._errorHandler,
      ),
    ),
  );

  constructor(
    private readonly _store$: Store<AppStateSlice>,
    private readonly _actions$: Actions,
    private readonly _http: HttpClient,
    private readonly _errorHandler: ErrorHandler,
  ) {}
}

Also see the full example app.

TODOs

  • Improve types
    • Make params truly optional
    • Make store$ truly optional
  • More
    • Documentation
    • Examples
  • Extend ideas
    • Identifiable entity-list
    • Partial resources like
      • Head vs. detail
      • Pagination
      • Search-results
    • Simultaneous storage of diff. results for diff. params
    • Easy idempotency
    • Easy-state accessors (like hooks)
    • Maybe helper to create a simple service layer