Angular NgRx – 2 Reducer, Selector, Effect

Reducer

we are going to implement reducer function that is going to take the user profile and is going to save it in the application state. The user profile will be thhere in memory ready to be used for the next time.

1. Define the content of our application state. This is the global state that is kept on the store in memory.So any state that is generated or maintained by the logging screens is kept here and there .


export interface AppState{
	auth: AuthState // This of property we are going to say that these of property in the application state is of type of state.
};

2. Define the type of state. These type here has a couple of properties(loggedIn, user)


type AuthState {
	loggedIn : boolean,
	user : User
}

This is how our application state is going to be structured as shown below.
index.ts


export interface AppState {
	auth : AuthState,
	//courses: CoursesState,
	//lessons: LessonsState
}

So for each of these properties of the application state we need to specify here a reducer function that is going to know how to take an action and produce a new instance of each of these state types.

The signature of this function :

index.ts


type AuthState = {
	loggedIn: boolean,
	user : User
}

export interface AppState {
	auth: AuthState
}

//The signature of this function 
function authReducer(state: AuthState, action) : AuthState {
	switch(action.type){
		case AuthActionTypes.LoginAction: 
			return {
					loggedIn: true,
					user: action.payload.user
				}
			default: 
				return state; //return the existing state without any modification
	}
}

export const reducers : ActionReducerMap<AppState> = {
	auth: authReducer
};

export const metaReducers: MetaReducer<AppState>[] = !environment.production ? []
 
login(){
	this.store.dispatch(new Login({user}));
}

The reducers functions are going to be called after dispatching an action and the goal here is to take the current state.

So that’s the first argument over reducer function the current state take, the action that was just dispatched.So that’s the second argument and return as the output of this function.

The new state we’re going to add here that the return type of these authentication reducer needs to be of type authentication state(AuthState).

So the goal essentially is to compute the new state that we are keeping inside the store as a response to a given action.

This function is called a reducers because its signature is identical to the reduce functional programming operation.

The first thing that we want to do is to identify exactly what action are we processing because these might be a logging action or a logout action in the future.
These could be any of the actions linked to the authentication module.

So if it’s a logging action then what should be the output of this function.One thing is for sure we need to output a new object which is going to be the new authentication state that we are going to keep memory in the store.

So let’s go ahead and create here a new object ({loggedIn, User}). So it’s very important not to mutate the existing object but to create a new one.

If we switch here to the definition of the logging action we can see that we have here a payload property with the user data so we can go ahead and retrieve here the use of data from the payload.

user: action.payload.user

Conclusion :

We have dispatched here the logging action to the store using the dispatch Method.

The store then took this action and tried to calculate a new state the way that the store is.

This is the story is going to take the current version of the application state.

Then the story is going to run all the reducers that we have defined here in the reducers function.

And for each of the properties of the state it’s going to run its own reducer.

How to define the Store Initial State ?

@ngrx/store/init

This action is going to cause all the reducers to be triggered and that is what is going to create the initial state.

So how can we specify an initial value for the authentication state. We know that the initial authentication state is going to be produced here by calling the reducer. We know that the reducer is going to be passed the value undefined.

So we are going to be using here the typescript Default Properties feature and we’re going to say that if the state argument is undefined then in that case we are going to initialize it with the initial state value.

state: AuthState = initialAuthState

This value is going to be a constant that we are going to be defining here.


const initialAuthState: AuthState = {
	loggedIn : false,
	user : undefined
}

So as far as we know the user is not initially logged in the next property that we’re going to specify is the user property which is going to be undefined.

This object is then going to be passed in here as the default value of the state parameter which means that the output of this function.

The first time that it gets called it’s going to be the initial of indications state object because we are going to fall here in the default clause case.


function authReducer (state : AuthState = initialAuthState, action) : AuthState {
	switch(action.type){
		case AuthActionTuypes.LoginAction: 
			return {
				loggedIn: true,
				user : action.payload.user
			}
		default : 
			return state;
	}
}

Reducer with initial State :
auth.actions.ts


import {Action} from '@ngrx/store';
import {User} from '../model/user.model';

export class Login implements Action {
	readonly type=AuthActionTypes.LoginAction;
	constructor (public payload : {user : User}) {}
}

export type AuthAction = Login;

auth.reducer.ts


export interface AuthState {
loggedIn: boolean,
user: User
}

export const initialAuthState : AuthState = {
loggedIn: false,
user: undefined
}

//The signature of this function
export function authReducer (state = initialAuthState, action : AuthActions) : AuthState {
	swtich (action.type){
		case AuthActionTypes.LoginAction : 
			return {
				loggedIn : true,
				user : action.payload.user
			}
		default : 
			return state; //return the existing state without any modification
	}
}
export const reducers : ActionReducerMap<AppState> = {
	auth: authReducer
};

export const metaReducers: MetaReducer<AppState>[] = !environment.production ? []

auth.module.ts


import * as fromAuth from './auth.reducer';
@NgModule {
	StoreModule.forFeature('auth', formAuth.authReducer)
}

we are using now storemodule.forfeature and we are linking our of integration reducer directly to the authentication module and not to the root module.

Besides store initial action (@ngrx/store/init) , we also have the new action (ngrx/store/update-reducers) this action is dispatched whenever one of our feature modules such as the authentication module gets registered.

One of the main advantages of registering our module using for feature is that it makes it really simple for our module to be lazy lothe if needed.

Logout Action :

We have seen here how to dispatch action using the store dispatch method and an action class.

We have also seen how to handle the action at the level of the store by implementing your reducer in
our of indication module. Now implement a second action(Logout).
logout action class

auth.action.ts


export class Logout implements Action {
	readonly type = AuthActionTypes.LogoutAction;
}

export type AuthActions = Login | Logout;

In our login component write our logout action, the logout action does not have any payload associated with it. We can now dispatch the log out actions so let’s go ahead and call dispatch method and we’re able to pass it a new instance of the logout action class that we have just created.

No you are dispatching these new actions but we are not yet handling it at the level of the store.

In order to handle the logout action we need to add an extra close to our reducer.


case AuthActionTypes.LogoutAction:
	return {
		loggedIn: false,
		user: undefined
	}

How will the logout action affect the state of the store. And the first thing that we are going to do is to set the logging flag to false because the user has just logged out because we no longer have any user credentials. Let’s set here the user profile to undefined.

How to access and use the ngrx store data ?

If you remember the store is an observable application state. So we have here the SUBSCRIBE Mefford and these observable. The store is easy meeting instances of application state these means that the value that we get here is a new instance of the application state which contains all the state of the store.

app.component.ts


constructor(private store: Store<AppState>) {
}

ngOnInit(){
	this.store.subscribe(state => console.log("state", state))
}

Using exchanges map operator we are going to take the state and we we’re able to extract from it only the
information that we need only the slice of state that we need in this case.

We only need to log the flag from the of substate. So this means that with this mapping function here we are no longer receiving via these observable the complete state.Instead we are receiving only the value of the log bin flag.


this.store
	.pipe(
		map(state => state.auth.loggedIn)
	)
	.subscribe(loggedIn => console.log("loggedIn", loggedIn))

If we want to drive here from these store observable the login state, we cand define new observable , it will be an observable of boolean that will be derived from the store.


isLoggedIn$ : Observable
isLoggedOut$ : Observable

Now we calculate the isLoggedIn observable , here we defining without the call to subscribe. Here the output of the pipe operator is an observable that is the result of applying call this pipe operation to the srouce observable which is the store itself.


this.isLoggedIn$ = this.store
			.pipe(
				map(state => state.auth.loggedIn)
			);

this.isLoggedOut$ = this.store
			.pipe(
				map(state => !state.auth.loggedIn)
			);

So far we discussed, how to derive slices of state they are actually from the store.

Example showing conditional login button
app.component.html


<a routerLink="/login" *ngIf="isLoggedOut$ | async">
	Login
</a>

this is going to subscribe to these observable and get the immediate values.So whenever the user is logged out we are going to be showing this button otherwise this button is going to be hidden.

we have created here manually a couple of observable of derived state from the store observable.

What is Ngrx Selector ?

Selector is simply a function which is going to be very similar to this
one this.store.pipe(map(state => state.auth.loggedIn)) that takes as input some (state) on the store and generates as output (state.auth.loggedIn) a slice or a subset of that
state.

So in that sense it’s very similar to a mapping function as we see here but it solves an important problem that we haven’t faced yet at this point.
We are going to be doing this calculation (map(state => state.auth.loggedIn)) of the United States in an optimized way if we keep getting back from the store the exact same authentication state that has not changed since last time.Then we are always computing the exact same result.

In this case the login Flag if this function of calculating (map(state => state.auth.loggedIn)) a derived state would be much larger than this one then that could potentially cause a performance problem because new versions of the state are going to be emitted by the store all the time not only when we do login and logout but with any other action of the application.We want this function to be triggered only if there is really something new to calculate.

So we are going to have it memorized the calculations that it did the last few times that is known in functional programming has memoization.

how can we define this mapping function with memory.

1) First create here a new of the selectors file that we’re going to add here to our of indication feature module in inside that we are able to start by defining a simple select.

The selector is going to be called select authentication state and like the name implies it’s going to be a function that takes the soul argument the complete state of the store and it’s going to get back as the result of this function the authentication object.


export const selectAuthState = state => state.auth

So this is known as a feature selector because it’s selecting one of the subtrees of State of the complete store application state.

2) Now using this feature selector as our starting point let’s go ahead and define here a mapping function a selector that is going to calculate here the login States itself.

we’re going to define here a new selector.We are going to call it is loggedin and we want this function to have memory. We want it to be a pure function that remembers calculations that it did before. So in order to create this memorized function we are going to be using this utility(createSelector) here from NgRx which is that create selector function.

This function takes as the first input arguments others select there so we can pass it in here.
The first selector that we have created (selectAuthState).


selectAuthState if we would have here are there selectors we could pass them here.

export const selectAuthState = state => state.auth;  // first selector

expoert const isLoggedIn = createSelector (
	selectAuthState,
	....., 	// selectors we could pass
	.....,
)

The last argument of this function call is not a selector function. This is what we call a projector function.

This is a function that is going to take all the outputs of all the select types that we are defining here on this list and it’s going to get as the result of Islam itself.

So in this case we are only passing in here one selector(selectAuthState) So we’re only going to get here one argument which is going to be the output of the selector(selectAuthState) which is going to be the authentication state(state => state.auth).

Now the output is going to be the result of (isLoggedIn) then.

So as we know this is going to be the (loggedIn) that is available inside of indications state (auth => auth.state).

And with these we have defined our first selector function these function that we have here is loggedIn is going to take as an input the complete store States (selectAuthState = state => state.auth) and it’s going to give us as output a boolean


export const selectAuthState = state => state.auth;

export const isLoggedIn = createSelector(
	selectAuthState,
	auth => auth.loggedIn
)

define our second selector. it does seem like there is going to be derived from the isLoggedIn selector.
So it’s our initial example of how to combine selectors together as usual we’re passing here and list of selectors. In this case we are going to be passing only one element which is going to be that is loggedin selector.

And here we are going to be defining our projector a function.The projector function is going to take as first argument the output of the first selector. So this is going to be a boolean flag that we are going to call it (isLoggedin) and the output is going to be the output of that is isLoggedout selector.So in this case we’re negating the result of the selector.

auth.selectors.ts


import {createSelector} from '@ngrx/store';

export const selectAuthState = state => state.auth;

export const isLoggedIn = createSelector(
	selectAuthState,
	auth => auth.loggedIn
)
export const isLoggedOut = createSelector (
	isLoggedIn,
	loggedIn => !loggedIn
)

We are going to be providing here the loggedOut state and with these we have quickly defined a couple
of selector functions that they extract a given slice of state from the store. These functions have memory, if they are called with the same input arguments. They are not going to calculate the output again. Instead they’re going to fetch directly the result of the calculation from memory and they are going to pass it directly without executing any of the selector functions or the projector function.

we will apply them into our component.
So we won’t be using here this mapping function anymore instead to derive the state from the store. We are going to be using the selector.


import {select, Store} from '@ngrx/store';

ngOnInit(){
	this.isLoggedIn$ = this.store
				.pipe(
					// map(state => state.auth.loggedIn)
					select(isLoggedIn) //Special RxJS operator that is available in ngRx store
				)
}

So these observable here isLoggedIn is not going to emit duplicate values of loggedIn as true.

This selector is only going to emit values whenever the value has changed from last time.

what is a side effect – NgRx Effects :

NgRx effects library from store.
For example in local storage or in a cookie we are going to be using local storage in ways that because we want those credentials to be injected transparently in the store if the user hits here the refresh button. We want the session to survive.

This is called a side effect because the action is not only affecting the state of the store via the reducer but the action is also producing some modification of state outside of the store.In this case our side effect is modifying the content of local storage. But this could also affect the database.

So a side effect is an usual way for synchronizing the state of the in memory store with the database transparently in the background without affecting the user experience.

Page refreshes saving the user credentials in local storage is an example of a side effect that we want our application to produce in response to the logging action. In summary what we need is a service that is continually checking what actions are dispatched to the store and in response to certain actions. It’s going to produce certain side effects.

auth.effects.ts


import {Actions, Effect} from '@ngrx/effects';

@Injectable()
export class AuthEffects {
	constructor(private actions$: Actions)
}

auth.module.ts


@NgModule ({
	EffectsModule.forFeature([AuthEffects])
})

NgRx Effects – step by step implementation of the Login and Logout Effects :

Now explain what actions observable is and how can we derive side effects from it. We have here the actions of observable(actions$) which is going to be emitting a new value each time that an action gets dispatched.

For example whenever we log into our application and we dispatch the logging action these actions observable is going to be emitting a value which is the logging action itself.

we are going to go ahead and generate a side effect to the login class.We are going to take the login payload and we are going to save it into local storage.

login$

This is going to be the observable that is going to produce our side effects and we are going to build
it based on the actions observable(this.actions$).

Reference

Leave a Reply

Your email address will not be published. Required fields are marked *