Get started with NGRX

Hi friends readers, this is a simple introduction to the use of ngrx/store with actions, reducers and selectors, we will talk about the effects and entities in another articles. I will skip all the parts related to why use a store manager, what is NGRX and other introductory notes, we will go directly to the core.

But let me talk a little in a very simple way about the concepts we’re going to deal with, what is the store, the actions, the reducers and the selectors.

The store is a container, it is a large (relatively) javascript object, in which we will read and write the data we are interested in. It is important to understand the store data behave like observables but we will talk about it later.

The actions are nothing more than events that happen in the application. They will be generated through our code through a function of the Store, once intercepted, the reducer’s code will perform write operations on the Store.

The reducers have the task of intercepting the generated actions and then performing write operations on the store.

The selectors have the task of recovering data from the store, they observe a portion of the store and it is possible to subscribe and receive updated data when they change.

The flow works like this: from the component we launch an action, the reducer intercepts the action and make changes to the store. Through the selector we recover the data ,that transmits them to the component interface, every time the data changes.

There are different strategies to organize the various dedicated files for actions, reducers and selectors, here we will not talk about it, surely you will find dedicated articles on the best practices for this topic.
For this simple app we will create an ngrx folder where we will put everything that concerns the management of the store.

Let’s start by writing our actions which is actually only one, ‘updateUser’, we will need it to define a data update action on the part of the store that manages a user’s data.

So let’s create an actions.ts file in ngrx folder which will have this code:

export const updateUser = createAction(    '[Home Page] update user',    props< {user:User} >())

This short piece of code will create an action with a description which is the first parameter and the second parameter with prefix props is in this case a User type object. The action description will be used when we use ngrx dev tools.

We continue observing the code of the reducers, which in this case too will be a single reducer, then we create the reducers.ts file in the ngrx folder which will have this code:

const initialState: AuthState = {    user: null}const userReducer = createReducer(    initialState,    on(actions.updateUser, (state, {user}) => ({ user:user })));export function reducer(state: AuthState | undefined, action: Action) {    return userReducer(state, action);}

We define a constant “initialState” that we will need to initialize the part of the store that interests us, we will see later when we talk about the module that this reducer will manage the part of the store called ‘auth’.
So with createReducer we create the reducer, giving the initial state as the first parameter and the following (in this case only one) will be the managers of the actions, which we will invoke in the main component, then the manager of the action has the first parameter as the action to be intercepted and the second parameter is a function with the current state and the user object (which we will pass to the action when we invoke it from the main component) as parameters, so there is the code to be executed on the store, i.e. we assign the user object to the user in the store.
Finally, the reducer function will serve as a parameter in the dedicated module to instantiate this part of the store.

Now the time has come to tell our app to create a portion in the store where we will interact to manage the user.
Let’s create a ‘module.ts’ file in the ngrx folder which will have this code:

@NgModule({    imports: [        StoreModule.forFeature('auth', fromUsers.reducer)    ],})
export class UserModule {}

We will use forFeature passing as ‘auth’ parameters which is the part of the store where we will store our user object and the reducer function that we have declared in the reducers.ts file.
But we need to tell the main module app.module.ts to load this module so in app.module we are going to insert this code:

imports: [
...
StoreModule.forRoot({ }), UserModule]

This is how app.module.ts should be. We instantiate the general store and import the UserModule that we called module.ts into the ngrx folder.

Now the time has come to write the selector, creating a file in the ngrx folder with the name of selectors.ts which will have this code:

const selectAuthState = createFeatureSelector<StoreState,
AuthState>('auth')
export const selectUser = createSelector(
selectAuthState,
(auth:AuthState) => auth.user
)

selectAuthState is a way to select the part of the store that interests us, in this case ‘auth’ of type AuthState, then we go to create the real selector, selectUser let’s say to select the state ‘auth’ and then the user property. We are going to use this selector in the main component.

Now finally the time has come to make our app work, for now we have prepared the ground.
Let’s see what’s in the app.component.ts component:

export class AppComponent implements OnInit{    user$:Observable<User>    userForm = new FormGroup({        name: new FormControl(''),        surname: new FormControl(''),        age: new FormControl('')    });    constructor(private store:Store<StoreState>){}    ngOnInit(){        this.user$ = this.store.pipe(select(selectUser))    }    updateUser(){        let user:User = {            name:String(this.userForm.controls.name.value),            surname:String(this.userForm.controls.surname.value),            age:Number(this.userForm.controls.age.value)        }        this.store.dispatch(updateUser({user:user}))    }}

Much of the code here is clear but we are going to comment it anyway. We create an observable user$ of type User which we will use it in the template to display the user data in the store.
We have a reactive form and then we inject the Store into the constructor.
At the NgOnInit() we go to use the selectUser selector, select is a function of ngrx / store.
So observing the code, in essence the store is an observable and we select a part of it, then we will subscribe to the template so we can have the updated data if they change.

Let’s see what’s in the html template.

<h1>ngrx/store demo1</h1><h2>User:</h2><div class="form" [formGroup]="userForm">    <input type="text" class="text" formControlName="name" placeholder="name" required>    <input type="text" class="text" formControlName="surname" placeholder="surname" required>    <input type="text" class="text" formControlName="age" placeholder="age" required>    <button class="btn" (click)="updateUser()">Update</button></div><h2>Selector user from store:</h2><pre>    {{user$ | async | json}}</pre>

We have a very simple form and then we print the user object in json format, given by the subscription of user$ with the async pipe

In conclusion I would like to invite you to consult the official guide of ngrx https://ngrx.io/ mine is clearly not an attempt to replace it but it is a way to shed some light on this instrument, it is a match to start, it’s a simple idea of how ngrx/store works . In the code I have not reported the imports to have a code as clear as possible, but you can still consult all the code on github and stackblitz. Enjoy yourself!

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Andrea Di Cioccio

Andrea Di Cioccio

Sono un ex sviluppatore di siti web, ora mi occupo di copywriting.