Within a single store? We are going to talk later about stores, but in a general definition, they have the responsibility of holding the state and applying changes to it when they are told to do so (when an action is dispatched, we are also going to talk about them later).
Dispatch an action?… We are going to talk about actions later but for a general point of view, they are identifiers of an operation on your application and they can be triggered (or dispatched) to tell the application to execute the operation that the action represents.
State in a redux app is immutable! So when a reducer changes something in the state, it returns a new state object.
How to add storeDevTool
@ngrx/store-devtools
npm install @ngrx/core @ngrx/store @ngrx/effects @ngrx/router-store --save
npm install @ngrx/store-devtools --save
Add below code into the “app.module.ts” file
import { StoreDevtoolsModule} from '@ngrx/store-devtools';
@NgModule({
declarations: [AppComponent],
imports: [
StoreDevtoolsModule.instrument({}),
],
STORE Architecture :
constructor(private store: Store){}
The store in the new implementation of the home component is going to be the source of all our data.
store.subscribe();
- Our store itself is observable
- We can subscribe to it and we can combine it with other observables the multiple values emitted by the store observeable are instances of application state(<AppState>).So the store is an observable of applicaton state.
#The store is an observable.We should not directly modify the data that it’s emitting.Instead if we want to modify the data that is available inside the store, we need to dispatch an action to the store.
store.dispatch();
The Store is a centralized Singleton Service that acts as an in-memory database. It is going to contain all our application state meaning both the data of the application and any other UI state that we will to keep on a centralized place.
We can not modify that data directly from the components in order to modify the way that instead we need to dispatch an action.
The store that are going to react to the action which is essentially an event and its going to in response to that produce a new version of the application state.
Create and dispatch first Store Action – Login Action
Implemntation of our first action :
import {Action} from '@ngrx/store';
export enum AuthActionTypes{
LoginAction = '[Login] Action',
LogoutAction = '[Logout] Action'
}
export class Login implements Action {
readonly type = AuthActionTypes.LoginAction;
}
export type AuthActions = Login;
We use the login action class in order to use the login action.To use the login action we need the store.
LoginComponent.ts
constructor(private store: Store<AppStore>){} //Inject the store in the constructor
//using the store , we will then able to dispatch new action. To do that we have linked here a new instance of Login action class.
Login(){
this.store.dispatch(new Login())
}
//The login action is only going to be dispatched after the user has successfully loged into the application.
user.model.ts
export interface User {
id: string;
email: string;
}
auth.actions.ts
import {User} from '../model/user.model';
export class Login implements Action {
readyonly type = AuthActionTypes.LoginAction;
constructor (public payload: {user: User}) {
}
}
login.component.ts
login(){
const val = this.form.value;
this.auth.login(val.email, val.password)
.pipe(
tap(user => {
this.store.dispatch(new Login({user}));
this.router.navigateByUrl('/courses');
})
)
.subscribe(
noop,
() => alert('Login Failed')
);
}
Store Development Tools for state management solutions : Provide a UI for inspecting the store and everything related to it.
Whenever we start an ngrx store application these initial action store init is going to be emitted by the ngrx store libarary itself.This action is necessary for initializing the store state.
@ngrx/store/init
Example :
[Login] Action -
{
payload : {
user : {
id: 1,
email: 'test@angular-university.io'
}
},
type : '[Login] Action'
}
Here the ‘State’ is {}(empty). So the store by definition whenever it receives an action will not by itself do anything with the action.
If you want the store to do anything with the action we need to write a ‘Reducer’ function.
This function is going to take the current state of the store which in this case it’s an empty object. Its going to take as a second argument action payload that we have above.So in this case our reducer function should take the user object here from the payload and it should add to the application state.
REDUCER Function :
Whenever we dispatch an action to the store we also have to inform the store what to do with a given action.For that we need to provide the store a reducer function.
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 there 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.
export const reducers : ActionReducerMap = {
};