Reactive State management in the angular — NgRx Store, actions, selectors

Irshad Sheikh
initgrep

--

Reactive state management in angular using NgRx framework

NgRx framework helps to build reactive angular applications.

  • NgRx Store provides reactive state management for the angular application. NgRx store is the redux implementation developed specifically for angular applications and provides RxJS observable API.
  • The state is an immutable data structure that is a single source of truth for the whole application.
  • NgRx Actions represent the unique events in the application which may be used to perform state transition or trigger side-effects.
  • NgRx Reducers are pure functions that react to Actions to perform state transitions.
  • NgRx Selectors are pure functions that select, derive, or compose a slice of the state.
  • NgRx Effects allow the isolation of side-effects.

Prerequisites

  • you have a fair understanding of the angular framework.
  • You have a basic understanding of redux architecture.
  • you have a fair knowledge of RxJS Observable API and various operators.

Installation

If you already have an angular app, you can directly go to step — 4

installation steps

To begin with, let us have a look at an example file structure. A structure like this would be helpful to split up each feature of NgRx state management in your app. I usually replicate the same structure in each feature module.

Example file structure for NgRx features
  • app.actions.ts file will contain the NgRX actions
  • app.effects.ts file will contain the NgRx effects.
  • app.reducer.ts file will contain the State design and its initialization. it will also contain a reducer function.
  • app.selectors.ts will contain the NgRx selectors.

Here is the complete project setup.

State

The state represents an immutable object that contains the state of an application. It is read-only, so every state transition will return a new state rather than modifying the existing state. As the application grows, each feature should contain a separate state which are part of the global app state. As such, the application state contains one or more feature states.

The state is similar to Javascript objects. It contains the feature states as the key-value pairs where the key represent a feature state and the value is the feature state object.

The state related to a feature module is referred to as feature state.

example root state

Let’s assume, our angular application has many feature modules. One of the features is responsible for the user’s profile. The profile module is responsible for rendering the list of users and the related posts .

To design the state, we can assume that the state required for the profile module should contain a list of users and List of posts. Let’s call the profile state as ProfileFeatureState.

We defined the type for User and Post and also created an interface for ProfileFeatureState.

Application state

Finally, we would add ProfileFeatureStateto applications root state - AppState. The profile key represents the profileFeatureState.

Initializing the state

Initialization of state

Initially, the state of the application is null since there would be no data. As such, both the users array and posts array would be initialized to null.

At this point, app.reducer.ts file should look like -

app.reducer.ts full version

NgRx Actions

NgRx Actions represent events in the application. They may trigger a state transition or trigger a side-effect in NgRx Effect services.

The Action interface contains a property called Type. The Type property identifies the action. Actions can also contain optional metadata.

Action Interface

CreateAction function

createAction function is used to create the actions and it returns an ActionCreator function. ActionCreator function, when called, returns an action of type TypedAction. Optionally, we can also supply additional metadata using the props function.

Let’s go ahead and create an action to add users to ProfileFeatureState.

addUsers action

Notice the type of addUsers action is [profile] add users. The [profile] represents the source of action. Also, the props contain the array of users as the metadata.

Similarly, we can create an action for adding posts to the feature state.

addPosts action

addPosts action is dispatched to indicate that the Posts should be added to the state. It will also contain Post[] metadata.

Actions represent the events and not the commands or operations . A single command or operation may generate many types of Actions. For example: An operation which creates a new user would atleast generate Actions for success and failure such as [profile] user created or [profile] user creation failed .

NgRx Reducers

Reducers are pure functions that perform transitions from one state to another state based on the latest action dispatched. The reducer functions do not modify the existing state, rather it returns a new state for every state transition. Hence all the reducer functions perform immutable operations.

createReducer function

NgRx provides a createReducer function to create reducers. It takes initialState as the first param and any number of on functions. The on function provides an association between actions and the state changes.

When an action is dispatched, all the reducers receive the action. The on function mapping determines whether the reducer should handle the action.

createReducer function returns an ActionReducer function . ActionReducer function takes an Action and a State as input and returns a new computed State.

Let’s go ahead a create reducer which handles transitions for ProfileFeatureState. createReducer function can map many actions and return an ActionReducer function.

createReducer function for the profile feature

The […] spread operator copies the properties of the object and returns a new object. It only performs the shallow copying and does not copy the nested structures. You should always consider a better alternative if you are dealing with a state that contains nested data structures. Libraries like lodash provide methods to clone nested structures.

ActionReducerMap provides the mapping as key-value pairs where the key represents the feature name as a string and the value is the ActionReducer function returned by createReducer function.

In our case, the ActionReducerMap will contain profile as a key and value as theProfFeatureReducer.

ActionReducerMap

It is not necessary to create an ActionReducerMap. You can directly provide the mapping in StoreModule.forRoot({key: ActionReducer})while registering the reducer in app.module.ts. You can also separately register the feature state in the feature module. I prefer creating the ActionReducerMap separately as it provides a better type checking in Typescript.

At this point, our app.reducer.ts file should look like :

app.reducer.ts file

Register the State

Once the reducer is created, It should be registered in the Module.The state can be registered using one of the two options:

Register Root state

To register the global store in the application, StoreModule.forRoot() takes ActionReducerMap as an argument. The map contains key and ActionReducer Object returned by createReducer function.

Register each feature state separately -

Feature states are similar to root states but they represent the state of specific features of an application. Typically, each feature should be registered in its own module.

If you have come this far. You might also want to read about NgRx Selectors.

  • How to create NgRx Selectors
  • How to use createSelector function to compose selectors with single or multiple slices of state
  • How to use a projector function to return only a part within the slice of state.

NgRx selectors are used to select a slice of state. I have a detailed post about it here.

This post was originally posted on initgrep.com

--

--