NgRx — Cherry Picking the Meta

w3ird0h
Angular In Depth
Published in
9 min readApr 5, 2019
Photo of cherries by Clem Onojeghuo on Unsplash

AngularInDepth is moving away from Medium. More recent articles are hosted on the new platform inDepth.dev. Thanks for being part of indepth movement!

This post is an amalgamation of techniques shared by the NgRx community, with just a step beyond the introductory nature of most of the great posts out there. Here are some of the things it will cover:

  • Action dispatches based upon navigation
  • Fetching data and handling errors
  • Gating Action dispatches (Skip unnecessary API calls)
  • Call State Reducers and Triggers (Handling Loading and Erroring States)
  • Entity Detection (This thing loaded?)

Here are the various libraries that are utilized:

Usage of the libraries and helper utilities will be showcased first. This is so we can get a feel for the developer ergonomics that the techniques help accomplish, and then we’ll follow up with some implementation details.

Deep details of setup and what each library does will be skipped, and there is a list of learning resources and docs in the conclusion.

The Base Reducer

What can we extrapolate from looking at that ☝️reducer?

  • There is a probably a top level state key called movies
  • There are CRUD helpers and a normalized storage mechanism available via @ngrx/entity for movies
  • When movies are loaded, they’re set in the state tree
  • We start with some initialCallState, whatever that is…

Our application is handling loading and erroring states for movies, it’s just not obvious yet.

When we need to handle CRUD scenarios for a movie in the state tree we interact with that base reducer as we normally would, and we let a callStateReducer handle loading and erroring states.

Here👇 is what the state tree would be initially:

When we navigate to the movies route an action will be dispatched and an API call will be made. The call state will reflect that an API call is in progress for a batch 👇 of movies:

After the movies are loaded the call state will again signify that it is no longer loading, but it’s 👇 back at a resting state:

The Call State Reducer

☝️That is the reducer that gets registered in a feature data access module 👇

The Call State Triggers

The baseReducer was passed to the callStateReducer along with these moviesCallStateTriggers. They are responsible for declaring when the call state gets triggered for a change.

  • For actions that kick off API calls, add the corresponding action to the appropriate loading collection, add as many as you need
  • If the API call was successful and data is loaded into the store, add those corresponding actions to the resting collection
  • Follow the same pattern for errors

Before looking at implementation details for call state we’ll switch to another piece of the puzzle, kicking off Actions and data fetching.

Dispatch Actions Upon Navigation

Nrwl — Nx Logo

What can we extrapolate from looking at that ☝️effect?

  • The DataPersistence library from Nrwl Extensions is being used
  • The MoviesFeatureShellComponent is setup in the Angular Router, because the DataPersistence navigation helper looks for the component to be routed to, and then executes the run callback
  • Work we want to associate with navigation has been separated from the component tree

Opinion: While ngOnInit is low hanging fruit for kicking off something to occur upon navigation, it introduces unnecessary coupling for our intentions and it’s limiting in comparison to what we get from moving the work into an effect.

  • We have access to the ActivatedRouteSnapshot in the run callback, as well as a readonly snapshot of the state tree
  • We only dispatch an Action to request all movies if none are loaded, mitigating redundant API calls

Note: This gating technique is meant as an example — be sure to adjust your gating logic to match your applications needs.

Here’s what a detail navigation trigger could look like 👇

Fetch Data and Catch Errors

A few notes here:

  • These data fetching effects are being triggered by the output of our navigation effects. Since the data fetching and navigation concerns are separated, we can update what happens for navigation without having to update what happens for data fetching 👏
  • The first parameter in the run callback for fetch is an Action, not the ActivatedRouteSnapshot like in the navigation helper
  • If an error occurs in run, the onError callback is called and the stream won’t blow up 🚫💥🤘

Quick Midway Recap

Photo of leaves and a neon sign that reads “and breathe” by Max van den Oetelaar on Unsplash

What have we covered so far?

  • Saved movies into the state tree
  • Separated associated navigation logic from the components (get outta here with that!)
  • Triggered a fetch of movies based upon navigation
  • Skipped unnecessary Action dispatches and avoided redundant API calls
  • Fetched a batch of movies in a list/summary view
  • Utilized the ActivatedRouteSnapshot to fetch a single movie
  • Reflected the loading state of an API call in the state tree
  • Caught errors from API calls and reflected that in the state tree

Which pieces are we missing?

  • Selecting information from the store to use in our component trees (are we loading? loaded? was there an error? where’s my data?)
  • Implementation details (moviesDetector, callStateReducer)

We’re also missing connection directly to the view and we’re not covering that in this post. See the resources list in the conclusion for posts that will cover that.

Exposing MoviesState Streams via a Facade

What can we extrapolate from looking at that ☝️facade?

  • All the information that we care about for movies is exposed as different Observable streams. The facade can be injected into the component tree and you can pull out what you need
  • We’re able to get the movieByRoute$ which means that router information is available at the selector level
  • We can communicate loading and erroring states to our component trees effectively for single or batch fetches of movies

Opinion: Facades are helpful. They can decouple your application from specific state management implementation lock-in, provide an easy-to-reason about API for data in your application, and potentially simplify testing. They’re also a helpful tool for refactoring if you are upgrading an existing app to use NgRx for the first time — do Facades first, then hook them up to a store. They are not required, and won’t always provide enough benefit to justify using the pattern.

Enabling the Facade with Selectors

What can we extrapolate from looking at these ☝️selectors?

  • There is a getCallStateSelectors factory function giving us access to all the callState selectors we care about
  • We have router information in the store so it can be used at the selector level, which helps us with the getMovieByRoute selector. This enables us to treat the router as a source of truth
  • We see our moviesDetector again that is helping us determine if a set of movies is loaded — note that it’s being used here at the selector level, and also some saw some usage in our navigation effects

Diving into Implementation Details

Underwater photo of a man diving into a pool by Artem Verbo on Unsplash

Let’s look first at the moviesDetector:

When I say entity detection, I’m referring to the act of checking for the existence of either a single specific entity or a batch, denoted by these types of checks:

  • allLoaded — batch
  • noneLoaded — batch
  • oneLoaded — single
  • notLoaded — single

You can see that the moviesDetector is still just wrapping around another helper, the entityDetectorMap. You could use the entityDetectorMap, or just the different entity detectors directly, and the purpose of the moviesDetector is to help with type safety and readability.

The Entity Detector — Implementation

Some of the goals with these entity detector helpers:

  • Type safety
  • Ergonomic — These should be easy to use, read, and reason about
  • Reusability

Here’s an example for a pageDetector, checking to see if some page information has been loaded:

The Call State Reducer — Implementation

👆That’s the shape and typings for callState, which we extend for slices of state where we want to use the callStateReducer 👇

Here👇 are all of the callStateReducer implementation details:

What can we extrapolate from looking at the that ☝️implementation?

  • State and Actions are being passed into the callStateReducer just as any other reducer
  • It updates state, but only updates state when the call state triggers we set up are present, and only touches the callState
  • This reducer is reusable for any number of state slices, lifting the micromanagement of the loading and erroring states out of the base reducers
  • The last step is that the base reducer is called, just like NgRx is expecting
  • The getCallStateSelectors factory function takes a feature selector and will return selectors for all the different pieces of callState that we would care about
  • An error would clear out automatically upon the next fetch / successful load of data

Thoughts on Single and Batch

I don’t often see a distinction made with loading and erroring states between a single or a batch set of entities being fetched. I make a distinction because it lets me dial in to create very distinct experiences, and I don’t “cross the wires.” When a single entity is being fetched, and the application also happens to fetch a batch of entities, I want those distinct fetching scenarios to be reflected accurately. A single entity being loaded should not signify that a batch of entities finished loading. This helps keep the atomic nature of Event Sourcing intact.

Thoughts on Gating Techniques

Every application can differ in loading expectations, so be sure to gate action dispatches in a way that matches your applications needs.

Thoughts on the DataPersistence Library

The DataPersistence library is full of helpful stuff, but it’s not required. You can work out the same patterns without it, you’ll just have to implement similar logic that the library is handling for you. It has some nice goodies like Optimistic and Pessimistic update helpers that I did not show off.

Conclusion

The techniques in this post are what I’ve cherry picked and adapted from the different solutions out there provided by the NgRx community. If they work for you, great! Be sure to adapt them to your needs.

Example: You might need to gate an API call for an entity based on a summary/detail already being loaded, for which you’d need to adjust the detection strategy that was showcased in this post.

It’s important to understand that these challenges (loading data, gating API calls, dealing with errors, selecting relevant information, etc) exist regardless of the state management solution or JavaScript framework/ecosystem we’re using. Understanding these challenges and working through solving them can be applied to any number of different projects and tooling, so it’s helpful to become familiar with the problem domain.

Deriving loading, loaded, and error states outside of storing information in the state tree is entirely possible and some people think that it’s the right way to do it. Don’t store what you can derive, and make derivations convenient. Do what’s right for your team. I suggest sticking to idiomatic and community driven solutions, they’re good for other people to pick up on and for you to come back to later.

I intentionally did not go into a lot of error handling other than setting any in callState because what we do with error information is highly dependent on the design of the error handling journey.

Check Out Pentacle

I put some time into Pentacle this year, which is an aggregate of idiomatic, battle-tested solutions with learning resources about reactive systems and tools like RxJS, Angular, NgRx and more.

Check out the intro post or peek at the currently available resources.

Read The Docs

Review These Posts

Follow These People

Find me elsewhere as @jsonberry on Twitter and Github

--

--