State Management with NgRx
Using NgRx for state management in an Angular application allows you to build out application flows that track unique events and manage the state of shared data in a reactive, explicit, and consistent way.
Overview
Nx provides a schematic to build out a new NgRx feature area that manages shared state.
The @nrwl/angular package has an ngrx
schematic to generate files that implement best practices when using NgRx for state management. This schematic generates source files that include enhancements to NgRx for data persistence strategies, and simplified testing.
The ngrx
schematic generates an NgRx feature set containing the following files:
actions
- Express unique events throughout your application.reducer
- Handle state changes from dispatched actions to perform state changes in an immutable way.effects
- Handle side effects for isolating external interactions from UI components.selectors
- Composable functions that select pieces of state and update when their inputs change.facade
- Optional class that provides further encapsulation of NgRx from your component.
The ngrx
schematic only provides a sub-set of schematics for the NgRx libraries. See @ngrx/schematics for the full set of available schematics.
Command
The following command is used to run the ngrx
schematic:
nx g @nrwl/angular:ngrx <featurename> --module=<path-to-module> --no-interactive [options]
The name
and the --module=
arguments are required. The no-interactive
option chooses the recommended defaults for the schematic, unless you override them.
The most common additional options are:
root
- Set up the initial NgModule imports for NgRx Store, Effects, Router-Store, and Store DevTools.facade
- Optional. If you prefer to further encapsulate NgRx from your components, add an injectable facade. See the blog Better State Management with Facades for details.
See the API Docs for detailed descriptions of all the available options. Also visit the NgRx website for more guides and documentation about the libraries.
Initial Setup
To get started with NgRx in an Angular application, you set up the root level store. As your application grows, you add feature level states, ensuring that your code follows a common pattern each time.
The example below shows you how to setup NgRx in the root of your application.
nx g @nrwl/angular:ngrx app --module=apps/<appname>/src/app/app.module.ts --root
The above command applies the following changes to the provided module:
- Registers
StoreModule.forRoot({})
in the imports array for state management, with recommended runtime checks enabled for maintaining immutable actions and state. - Registers
EffectsModule.forRoot([])
in theimports
array for isolation of side effects. - Registers
StoreRouterConnectingModule.forRoot()
in theimports
array for integration with the Angular Router. - Registers
StoreDevtools.instrument()
in theimports
array for integration with the Redux Devtools browser extension.
You manage separate slices of state using libraries and feature states.
Feature Workflow
When building new features using NgRx, you want to manage the state from within a separate library. This allows your state to be easily shared across other libraries and applications. The steps below go through the workflow of using NgRx within the context of a library.
The example below generates a library to begin a new feature. For this example, products
is used as the library name.
nx g @nrwl/angular:lib products
To manage the feature state:
- Use the
ngrx
schematic with the feature name in plural form, such asproducts
. - Provide a path to the
products
library module.
nx g @nrwl/angular:ngrx products --module=libs/products/src/lib/products.module.ts --directory +state/products --no-interactive
Use the --facade
option to generate an injectable Facade class along with the feature.
The following files are created, or updated:
myorg/
├── apps/
└── libs/
└── products/
└── src/
├── lib/
│ ├── +state/
│ │ ├── products.actions.ts
│ │ ├── products.effects.ts
│ │ ├── products.effects.spec.ts
│ │ ├── products.facade.ts # optional
│ │ ├── products.facade.spec.ts # optional
│ │ ├── products.models.ts
│ │ ├── products.reducer.ts
│ │ ├── products.reducer.spec.ts
│ │ ├── products.selectors.ts
│ │ └── products.selectors.spec.ts
│ ├── products.module.spec.ts
│ └── products.module.ts
└── index.ts
The above command also does the following changes:
- Updates the feature module and registers
StoreModule.forFeature()
with the name of your feature state in theimports
array. - Updates the feature module and registers
EffectsModule.forFeature()
in theimports
array.
The feature library's barrel index.ts
is also updated to export the updated public API for the state including:
- The NgRx selectors.
- The NgRx feature reducer.
- The optional facade class for the NgRx feature.
When generating multiple feature states within a single library, make sure there are no naming collisions in the barrel index.ts
file.