NgRx Tutorial - Guide to Starting with NgRx
NgRx provides reactive state management utilizing RxJs and a global state object. In this guide, I'll explain how to implement the State, Actions, Reducer, Selectors, Effects, and how to use in a components.
Goals - Understanding the Following
- Why NgRx and Redux
- State Object
- Actions
- Reducer
- Selectors
- Effects
- Using in Components
Why NgRx and Redux
Angular architecture depends heavily on the observer pattern with heavy usage of RxJs
. This pattern reveals itself useful when consuming the HttpClient
and EventEmitters
in components, passing data to REST endpoints and through components.
Sharing data between components presents difficulties when objects mutate. Object references change or differ between the components after mutation. This lead to the infamous Facebook Notifications count bug. Using the Redux pattern presents a unidirectional flow of data, and with its source of truth in a global state object, one immutable object reference is sliced and pushes through to the different observers.
Using this pattern also can help prevent numerous round requests to REST services, cutting network chatter.
For a more advanced view of how to architect a system using NgRx refer to Architecting Angular Applications with NgRx.
Love Web Development?
Angular, Google Cloud, C#, Node, NestJs?
For a visual of this flow, reference the graphic below.
State Object
The state object is a global JavaScript object encompassing any properties required for the application. As users interact with the application, the global state object changes, in an immutable fashion through the Reducer, and pushes changes down through to a component's observables and subscriptions.
A simple implementation of a state object exists below.
The podcastId
can be consumed through selectors
and pushed down to the different consuming components. In my case for this state object, I use it as filter as the selected podcast in the user interface.
A more complex example using the EntityAdapter
, a class within the @ngrx/entity
package, exists below.
The EntityAdapter
adds the following properties:
ids
- an array of ids from the entities defined per theselectId
entities
- defines a dictionary of the entities through the id defined in theselectId
,{[selectId: string]: entity }
Actions
Actions are events that happen as a result of user interaction with the application. Using our podcastId
state example above, a user could select a specific podcast and the application would filter based on this specific podcast.
The Action
class always includes a type
property representing the action being dispatched.
The class SelectPodcastAction
includes a type property of [alsoa.ui.podcast.component] SELECT_PODCAST'
, along with payload
property of type string. The payload represents the action data associated with the action necessary to complete the action.
Free Your Developers
Nx Monorepo Starter alleviates developers from re-inventing deployments on popular Google Cloud Services, standardizes on libraries, and saves time for scaffolding projects.
Thanks for your interest in Nx GCP Starter. We'll be in touch shortly
Reducer
The reducer generates a new state based on the action dispatched and any payload information contained within the action. These files contain a switch
statement for any action that changes and returns the new state. Get familiar with the spread syntax as a mechanism to preserve immutability.
Selectors
Selectors provide a method to read slices of the state.
Combining selectors to retrieve slices of state and filtering through necessary data can also be achieved. In the selector below, the selected podcastId
filters through a dictionary of podcasts
to retrieve a specific podcast
.
In the selector above, the selectors selectFeatureState
and PodcastSelectors.selectEntities
combine. In the selectedPodcast
selector, we retrieve the selectedPodcastId
and a dictionary of entities
from the PodcastSelectors.selectEntities
in the end providing the selected podcast via entities[selectedPodcastId]
.
Effects
Effects exist to change or retrieve the state of an external system. For most of my use cases, effects communicate with a REST endpoint to query, insert, update, and delete different entities. Effects begin listening immediately for one or multiple actions.
In the case of real time communications, effects can open a stream from a service and dispatch events to modify the entity states accordingly.
In the effect above, a Firebase query opens a socket and events push through updating the state as it requires a little bit of configuration per the actions and reducer, but Firebase eliminates the difficulties of the broadcasting the type of mutation on the entity.
Component Usage
As the state changes, components can afford using OnPush
change detection giving a performance boost.
In order to read data into our component, inject the Store service into the component. Create an observable property to read a slice of data from a selector. I prefer not to explicitly subscribe to the observable. There exist better methods handling the subscriptions. My preferred method uses the async
pipe in the template alleviating the responsibility to explicitly unsubscribe in the component.
Summary
You should now have a better understanding of the Redux pattern, its implementation within NgRx, and how to implement it within your components.
With your understanding of NgRx and Redux, you'll need a strategy and pattern on implementing this in your application. Next, I explain how to Architect an NgRx application.