Redux introduction with NgRx (part 5): NgRx syntax explained

Redux introduction with NgRx

This article is part of the serie of articles that you may want to read before starting this one.

Here is the summary:

  1. Store & Application State
  2. Redux Principles
  3. Redux core concepts
  4. NgRx high level introduction
  5. NgRx syntax explained (you’re here)

NgRx Syntax and awesomeness

Actions

Declaring Actions in ngrx is not what you saw in most of the Redux tutorials. In fact ngrx is tailored for Angular and so written completely in Typescript. This will leverage all the super powers of working in a Typed environment.
The way ngrx/store is declaring Typed Actions are done with interfaces and classes.

Let’s see an example:

import { Action } from ‘@ngrx/store’;
export enum TodoActionTypes {
ADD = ‘[TODO] ADD’,
REMOVE = ‘[TODO] REMOVE’
}
export class Add implements Action {
readonly type = TodoActionTypes.ADD;
constructor(public payload: string) {}
}
export class Remove implements Action {
readonly type = TodoActionTypes.REMOVE;
constructor(public payload: number) {}
}
export type TodoActionsUnion = Add | Remove;

A lot of things happened here, let’s deconstruct a little.

First, you have the Enum declaration that will handle the type of the Action’s type property.

The Action itself is declared by the class ES6 keyword and we see that this class is implementing the Action interface from ngrx/store. This Action interface enforces the respect of the type property in your Action class.

We can see that the type property of the Action class is set to readonly, again to make sure not mutation can be done by error. If something try to mutate this property, the Typescript compilation will fail.

We set the payload as a constructor parameter because we will dispatch these Action by instantiating them like this:

new Add(‘My todo label‘);

Dispatch an Action

In our component or service, we will use the ngrx StoreService to dispatch an action. Here an example:

// Inside a component’s method
this.store.dispatch(new TodoActions.Add(‘My todo label‘));

We finalize by exporting all the Class interface we created into a Union type, the ActionsUnion. This will allows us to be able to have type checking and type safety when we will need to use them in the Reducers.

Reducers

Reducers are the place where the Application State is transformed. They get the current state and the Action as arguments and have business logic to do the manipulation.

A reducer function looks like this:

import { TodoActionTypes, TodoActionsUnion } from ‘./todo.actions’;
export function reducer(state: number = 0, action: TodoActionsUnion): State {
switch (action.type) {
case TodoActionTypes.ADD: {
// State manipulation to create a new state
return newState;
}
case TodoActionTypes.REMOVE: {
// State manipulation to create a new state
return newState;
}
default: {
return state;
}
}
}

Because we created the TodoActionsUnion and we are now setting it as the type of the action argument, we have the right type of action in each switch statements. The types will flow into our reducer normally, this is really non negligible to have type safety and type checking inside your manipulation logic of your State Management System.

Selectors

Selectors are methods used for obtaining slices of store state. ngrx/store provides a few helper functions for optimizing this selection. These pure functions can be composed together to make more complicated selector. They are optimized way to get a specific data from the Store.

You can create a basic selector like this:

this.todos = store.pipe(select(‘todos’));

Combining selector looks like this:

export const selectFeature = (state: AppState) => state.feature;
export const selectFeatureCount = createSelector(
selectFeature,
(state: FeatureState) => state.counter
);

Effects

First things first, Effects in are not present in the ngrx/store library, this is another npm package from ngrx.

Effects are used to deal with Asynchronous data flow. Effects are a tool specifically design to handle side effect of Actions, or asynchronous Actions, in a more simple way.

They listen for actions dispatched from ngrx/store, meaning that we can dispatch something like a LOAD Action and listen to this action into an effect executing a particular code.

They isolate side effects from components, allowing for more pure components that select state and dispatch actions.

They provide new sources of actions to reduce state based on external interactions such as network requests (Angular’s http for example), web socket messages and time-based events.

Effects flow

We can see the effects as a addition next to the Redux flow and thus, they have their own way to interact with the Store. Let’s see how it is done.

Unidirectional flow with side effect
  1. Our Effect is listening on a specific Action;
  2. When this Action is dispatched, the effect kicks in and perform its business logic. Here the effect call a method on a service/resource in the goal to get data from an API;
  3. The service does a simple external call to an API somewhere on a server;
  4. The API sends back some JSON response to the service;
  5. The service sends back the response to the effect business logic;
  6. The effect will then dispatch a new action with the right data to update the reducer.

Effects are a way to update the Store with asynchronous call. They are triggered by Actions and send their data to the reducers via dispatching Actions. They are not part of the Store.