Those who worked with NgRx before version 8 were used to create actions in the following way:

We all remember the amount of boilerplate that was needed to support our actions and reducers. With NgRx 8 it became much easier:

Instead of having an enum and a class for each type of action, now we need to call a single function createAction . Alex Okrushko has a great article which covers benefits of new action creators.

So how do they work?

First, let’s see what TypeScript gives us when hovering over logout action:

Our login is of type ActionCreator<T,() => TypedAction<T>>. The first parameter is generic parameter T , in our case it is “[Auth] Logout”. The second is a function which returns TypedAction<T> . TypedAction extends Action by adding readonly type field:

Now, let’s look on the implementation of createAction function:

We pass in type (‘[Auth] Logout’ in our case) and get back a Creator function — () => ({type: '[Auth] Logout'}) . On line 7 we call defineType function with type and our creator. defineType returns a passed in Creator function and adds a new property type to it. So, we have a function with type property, and which returns an object with type property 😵😕.

By doing this trick we can call logout.type and logout().type and they both return ‘[Auth] Logout’.

Let’s check what our new reducers are doing:

Here is the implementation of `on` function:

It first extracts reducer function which is the last argument (line 2), gets type of our ActionCreator (line 4) (that’s where the magic of defineType is used) and returns an object with reducers and corresponding action types (line 7).

Later on, in createReducers function:

Reducers and actions that we get from on functions (line 3) are built into a map of action type and reducer (line 5). A new reducer function is returned (line 7). If dispatched action is in the map, then it executes associated reducer otherwise original state is returned.

Summary

First, we define action creator:

export const login = createAction(

'[Login Page] Login',

props<{payload: {username: string; password: string;}}>(),

)

Then we define a reducer, it executes ons functions and builds internally a map of action type and reducer functions:

export const reducer = createReducer(

initialState,

on(AuthActions.login, (state, {username}) =>

({...state, user: username}))

);

Then we call in our component or service:

this.store.dispatch(AuthActions.logout())

Action creator function AuthActions.logout() is executed and we get a TypedAction back with type property equal to ‘[Login Page] Login’.

Reducer then goes through its map and tries to find a reducer function for a given type. When reducer is found it is executed, otherwise state object is returned.