Introduction
NgRx is a framework for building reactive applications.
It is Redux pattern tailored for Angular.
The purpose of NgRx is to provide a formal pattern for
- Organizing State: our application state into one single local state container.
- Managing State: by requiring a one-way data flow.
- Communicating state changes: to our components so they can react accordingly.
Key Concepts
@ngrx/store: Store is RxJS powered global state management for angular apps.
Actions: describe unique evens that are dispatched from components and services.
Reducers: state changes are handled by pure functions called reducers.
Selectors: are pure functions used to select, derive and compose pieces of state.
State: is accessed with the state, an observable of state and an observer of actions.
– for Local State Management Try using NgRxComponentStore.
NgRx State Management Life-cycle.
Note: All actions that are dispatched within an application are always first processed by the reducers before being handled by effects.
You can check more details on the official website.
NgRx – The Redux Pattern
Patterns brings order to chaos.
Redux is a predictable state container for JavaScript application.
Redux principles:
- Single source of truth called the Store.
- State is read-only and only changed by dispatching Actions.
- Changes are made using pure functions called Reducers.
Lets explore a little bit further
Subscriber Content
Store
is literally a JavaScript object
- Un-shared state remains in component.
- Angular forms are self contained, so those are also not in store.
Actions
All relevant user events are dispatched as actions, affecting reducers who update the store.
e.g. login action, retrieve-data action, spinner etc.
Reducers
Reducers are pure functions that specify how state changes in response to an action.
Examples:
- Set a userDetails property on Login.
- Toggle a sideMenuVisible = true on btn_click.
- Toggle showProductCode to show/hide codes.
- Set successfully set retrieved data on component initialization.
- Set global spinner property.
- Manage side-effects with NgRx-Effects library (do not use reducers for side-effects).
Selectors
are just pure functions used to obtain data from store.
NgRx – First Look
NgRx is composed of packages and only required package is @ngrx/store .
This package provides an in-memory container for our application’s state. Think of it as a simple JavaScript object holding the application state :
As we add more features to our application, this object can become bigger and bigger and difficult to manage, so what can we do?
The state is much easier to manage if it is arranged into a logical structure e.g.
here state is laid out by features. These pieces of state are sometimes called slices.
Installing The Store
We can use following commands to add ngrx store to our angular application.
npm install @ngrx/store --save
or
ng add @ngrx/store
which will install store and also add store import in root app module in our angular application.
as we can see the package is installed and also it updated the app.module.ts file as follows
The first argument to .forRoot is a reducer and the second is an optional config object. For now, both of those are initialized with empty object.
Feature Module State Composition
Just like we split the state into slices to manage it easily; we’ll have multiple reducers; one for each feature slice of state.
the first argument .forFeature is the state-slice of the feature and second argument is productReducer which is currently an empty object.
(Notice methods .forRoot and .forFeature; which are very similar to router methods.)
We could break our state down even further and build multiple reducers for one feature slice. However, for simplicity, we’ll use one reducer per feature.
So we have store wired-up to products feature module, lets define state and corresponding actions next.
Defining The State and Action
We’ll start simple, we define just one piece of state and one action to mutate that state as follows
- state: showProductCode
- action: toggleProductCode
This will help us understand the flow and later we’ll add more state and actions.
We also need a reducer to process this action and create new state.
Reducer
A reducer is specified with an exported constant using CreateReducer function.
We often create one reducer for each feature slice of state.
The purpose of the reducer function is to listen for actions, and for each action, handle the transition from current state to new state in an immutable way.
Lets put all these learning to practice with a basic demo.
NgRx Demo
Starting Code
I’ve a simple component as follows
There is a checkbox control on the user interface, which user can check or un-check to show/hide product codes (showProductCode local property) . We’ll update this code to hook-it up with NgRx sate management in this demo.
First question, where are we going to put NgRx code?
We can organize our state, actions and reducers by feature as well.
and following is the code for productReducer
with on function, we can wire-up actions with handler code.
(We’ll strongly type actions and clean-up this code a little bit later).
now we can reference this reducer into product module as shown below
Next, lets see how we can dispatch actions which will be processed by this reducer.
Dispatch Action to Change State
We often dispatch actions from our components based on user events.
For that, we must inject the store into that component (just like any service). Then we can use it to dispatch action as shown below.
Subscribing to the Store to Get State Changes
To access the value in the store, we select appropriate slice of the state.
and with that, we have all necessary pieces in-place, we can run the application and test the checkbox behavior to show/hide product codes via NgRx state management cycle.
Tip: because store is observable, we could’ve used its pipe method; however the best practice it to use select method and shape the results in the selector, not in the component.
Next, lets check some tools to make debugging easier.
Developer Tools and Debugging
We can install the Redux DevTools in three simple steps.
- Install chrome browser’s Redux DevTools Extension (tool for visual analysis and debugging)
- Install @ngrx-store-devtools package in application (package which expose NgRx state, actions etc. to DevTools extension)
- Initialize @ngrx-store-devtools module in app module.
we can install extension from chrome web store as shown below:
once installed we’ll see a button on browser as follows
which stays grayed out until we visit a site which exposes its state and reducer (normally only in dev mode) we’ll see icon light-up like a Christmas tree. We can click the icon and it will open-up a window of Redux DevTools. We can use CTRL + SHIFT + I to pin it in chrome dev-tools.
Step-2: Install ngrx/store-devtools package
@ngrx/store-devtools package can be installed as follows
ng add @ngrx/store-devtools
coz we used ng add command, the app.module is also got updated with following
with this, we are exposing our application state to outside.
Now, if we run our application and click the redux tool icon the following window shows up
and as we can there are two system level actions shown in the left panel; we can click one and then on the right-panel click Action button to see details:
Similar way, If we click on the second action (update-reducers), we can see our products feature listed there:
if we want to see the effect of this action on the whole state tree, we can click the state button as shown below:
at the moment, we’ve our initial state loaded. Now, if we un-check the showProductCode checkbox, we can see is effect in dev-tools as follows:
we can click back and forth to see the state at that point in time.
Now, if we click this checkbox several time, we can use time-travel debugging to replay our application state and notice the UI will be also updated accordingly with each action.
There are many other useful functions available in this tool you can check.
We’ll now stop at this point and next article will cover how to strongly type our application state and actions.
The source code of this application is available on this git repository and online deployed application can be accessed from this URL.
Summary
In this post, we covered the first steps towards centralized state management for our application using NgRx which is Redux pattern tailored for Angular.
We installed the NgRx store and wire-it up in our application. We saw, how we can structure our application state in small slices for easy maintenance using Feature Module State Composition which is very similar to how we structure our application in multiple features.
We installed Redux DevTools and saw how this helps us with advanced debugging of our application.
Let me know if you have some questions or comments. Till next time, happy coding.
Discover more from Hex Quote
Subscribe to get the latest posts sent to your email.