Announcing NgRx Version 8: @ngrx/data, creator functions, run-time checks, and isolated tests
Today we’re happy to announce the version 8 release of the NgRx platform. This release contains a lot of new features across most of the libraries, bug fixes, and some breaking changes, all aimed at improving the developer experience when using NgRx libraries.
@ngrx/data
The long-awaited angular-ngrx-data library by John Papa and Ward Bell has been integrated into the platform as a first-party package. NgRx Data is an extension that offers a gentle introduction to NgRx by simplifying the management of entity data while reducing the amount of explicitness.
For more info head over to the docs.
@ngrx/store
createAction, actions without enums, classes and union types
In previous versions of NgRx in order to create an action, you had to create an action type, create a class, and lastly, create an action union. Because we encourage the use of Good Action Hygiene, introduced by Mike Ryan, we had to come up with an easier way to create unique actions.
To allow you to create actions in a less verbose way, we’re introducing the createAction
factory function. The first parameter of the createAction
function is the action type, the optional second parameter are the extra metadata, or `props` of the action.
To compare both, the below image illustrates how you can use the new createAction
function on the left and on the right there is the current way of creating actions.
It’s also possible to use a function as the second parameter, this allows you to put logic inside the action creators.
export const addBook = createAction(
'[View Book Page] Add book',
({ bookId, amount = 1 }: { bookId: number; amount: number }) => ({
bookId,
amount: amount <= 0 ? 1 : amount,
timestamp: Date.now,
})
)
createAction
is a factory function that returns a function, called an ActionCreator
, which returns an Action
object when called. To dispatch an action, you have to invoke the ActionCreator
:
this.store.dispatch(addBook({ bookId: 47 }));
We refactored the example-app to use createAction
to give an idea of how it looks like and what is possible with it. For an in-depth look, see a previous post NgRx: Action Creators redesigned by Alex Okrushko.
createReducer, a reducer without the switch case
The new createReducer
function allows to create a reducer without a switch statement. It uses the on
function to make a distinction between the action types, and returns a new reference of the state. As you can see in the image below, it isn’t needed to handle a default case for unhandled actions in the reducer.
We refactored the example-app to use createReducer
to give an idea on how it looks like and what is possible with it.
More safety with runtime checks
To guide developers to follow the NgRx core concepts and best practices, we have added a four (4) runtime checks. They are here to shorten the feedback loop of easy-to-make mistakes when you’re starting to use NgRx, or even a well-seasoned developer might make. These checks are opt-in and can be enabled via the config of the root store.
The four (4) checks that are currently available are:
strictStateImmutability
: to verify that the state isn’t mutatedstrictActionImmutability
: to verify that actions aren’t mutatedstrictStateSerializability
: to verify if the state is serializablestrictActionSerializability
: to verify if the actions are serializable
@NgModule({
imports: [
StoreModule.forRoot(reducers, {
runtimeChecks: {
strictStateImmutability: true,
strictActionImmutability: true,
strictStateSerializability: true,
strictActionSerializability: true,
},
}),
],
})
export class AppModule {}
These runtime checks are a replacement for the
ngrx-store-freeze
package.
For more info head over to the docs.
Isolated tests with mock selectors
In NgRx version 7, we introduced MockStore
but this didn’t allow to create fully isolated unit tests. With mock selectors, in combination with MockStore
this becomes possible. These mock selectors are available via @ngrx/store/testing
. To provide selectors, define them while providing the MockStore
via the selectors
property which accepts an array of mock selectors.
import { provideMockStore, MockStore } from '@ngrx/store/testing';TestBed.configureTestingModule({
providers: [
provideMockStore({
selectors: [
{ selector: fromBooks.getSearchQuery, value: '' },
{ selector: fromBooks.getSearchResults, value: [] },
{ selector: fromBooks.getSearchLoading, value: false },
{ selector: fromBooks.getSearchError, value: '' },
],
}),
],
});
It’s also possible to define or override a selector with the new overrideSelector
function on MockStore
. The first parameter of overrideSelector
is the selector function, the second parameter is the result.
const store: MockStore = TestBed.get(Store);
store.overrideSelector(fromBooks.getSearchResults, [])
Besides creating mocked selectors via the mocked stored it’s also possible to set the result of a selector without the use of MockStore
. By using the the setResult
function of a selector, we skip the selector’s invocation and simply return set result. The setResult
function expects the result as parameter.
fromBooks.getSearchResults.setResult([]);
Thanks to John Crowson for working on this feature. For more info head over to the docs.
@ngrx/effects
createEffect for type safety
As alternative to the @Effect()
decorator, NgRx 8 provides the createEffect
function. The advantage of using createEffect
is that it’s type safe, if the effect does not return an Observable<Action>
it will give compile errors. The option { dispatch: false }
still exists for effects that don’t dispatch new Actions, adding this option also removes the restriction that an effect needs to return an Observable<Action>
.
We refactored the example-app to use createEffect
to give an idea on how it looks like and what is possible with it.
Automatically resubscribe on error, a safety net
Many effects are used to call API services and most of the time they result in Success
or Error
actions. For the Error
actions we noticed that this path is often not handled, or is handled incorrectly. When this happens the Effect will silently be destroyed.
Starting from NgRx 8 we will, by default, automatically resubscribe to the effect when this happens. This adds a safety net for where the unhappy paths were missed.
It’s possible to turn this feature off by setting resubscribeOnError
to false
at the effect level.
// with createEffect
login$ = createEffect(() => ...), { resubscribeOnError: false });
For more info, head over to the docs.
@ngrx/router-store
RouterState.Minimal, a fully serializable router-store
To make use of the serializability runtime checks a fully serializable router state is needed. To enable this we have provided a routerState
config property on the router store config. To turn this feature on, set the routerState
config to RouterState.Minimal
. The default value is RouterState.Full
and will use the current DefaultRouterSerializer
to serialize the Angular router events.
StoreRouterConnectingModule.forRoot({
routerState: RouterState.Minimal,
});
For more info head over to the docs.
Router selectors, a set of common selectors
Often, when working with @ngrx/router-store
there’s a need to select data from its state. With these new selectors, you can select the most common data from the router state. If you’re familiar to the entity selectors, these new router selectors will look familiar.
export const selectRouter = createFeatureSelector<
State,
fromRouter.RouterReducerState<any>
>('router');const {
selectQueryParams, // select the current route query params
selectRouteParams, // select the current route params
selectRouteData, // select the current route data
selectUrl, // select the current url
} = getSelectors(selectRouter);
Thanks to Jason Hodges for working on this feature. For more info head over to the docs.
Schematics
The schematics for the above create
functions are ready to use but are currently opt-in. To use them, use the creators
flag:
ng generate @ngrx/schematics:action ActionName --creators
ng generate @ngrx/schematics:reducer ReducerName --creators
ng generate @ngrx/schematics:effect EffectName --creators
ng generate @ngrx/schematics:entity EntityName --creators
ng generate @ngrx/schematics:feature FeatureName --creators
Thanks to Itay Oded for working on this feature. For more info head over to the docs.
Visual Studio Code Snippets
To further enhance developer ergonomics, we have contributed a series of NgRx snippets to the Angular Snippets VS Code extension by John Papa. These snippets are all prefixed with a-ngrx-*
and provide a convenient way to quickly generate actions, reducers, effects and selectors using the new version 8 syntax.
More information on the extension and snippets can be found at Angular Snippets (Version 8).
Team updates
Our team keeps on growing, this time we are happy to announce that Alex Okrushko and Wes Grimes are joining the NgRx team. With Alex and Wes joining the team we can provide better support, maintain and grow our docs at ngrx.io, and continue to build out the platform. We also welcome new ideas, so if you have any, feel free to open an issue.
Upgrading to NgRx 8
To start using NgRx 8, make sure to have the following minimum versions installed:
- Angular version 8.x
- Angular CLI version 8.0.2
- TypeScript version 3.4.x
- RxJS version 6.4.0
NgRx supports using the Angular CLI ng update
command to update your NgRx packages. To update your packages to the latest version, run the command:
ng update @ngrx/store
For an easy transition coming from ngrx-data
to @ngrx/data
install this package via the Angular CLI ng add
command:
ng add @ngrx/data --migrate
Breaking changes
This release has a couple of breaking changes. For most of these breaking changes we’ve provided a migration that automatically runs when you upgrade your application with the Angular CLI commands above.
Below you can find a rundown of the breaking changes, for more info and guidance, take a look at the V8 migration guide. The complete CHANGELOG can be found on our GitHub page.
@ngrx/store
- The
META_REDUCERS
token has been renamed toUSER_PROVIDED_META_REDUCERS
. TheMETA_REDUCERS
token has become a multi token and can be used by library authors - Selectors with only a projector function aren’t valid anymore, this change will make the usage of selectors more consistent
- The return type of the
createSelectorFactory
andcreateSelector
functions are now aMemoizedSelector
instead of aSelector
- The
ngrx-store-freeze
package has been deprecated in favor of runtime checks
@ngrx/router-store
- Usage of
forRoot
is now required forStoreRouterConnectingModule
@ngrx/entity
- add
undefined
to Dictionaries index signature, dictionary could be producing undefined but previous typings were not explicit about it - If an error occurs (or is flattened) in the main effect’s pipe then it will be
reported and the effect is resubscribed automatically
@ngrx/store-devtools
actionsWhitelist
is renamed toactionsSafelist
actionsBlacklist
is renamed toactionsBlocklist
@ngrx/data
NgrxDataModule
is renamed toEntityDataModule
NgrxDataModuleWithoutEffects
is renamedto EntityDataModuleWithoutEffects
NgrxDataModuleConfig
is renamed toEntityDataModuleConfig
Collaboration to support diversity and inclusion
Open source provides many opportunities for people of diverse backgrounds to contribute to tech. With This Dot’s Open Source Apprentice Program, led co-founder Tracy Lee, we collaborated with Marien Scott, with mentorship provided by James Spivey to incorporate visual testing of our example application using Applitools. This effort was a success and shows the potential that can be achieved when providing opportunities to under-represented minorities in tech. If your company is interested in taking part in these efforts to change the ratio, visit ThisDot, and hire the fempire.
Contributing to NgRx
We’re always trying to improve the docs and keep them up-to-date. To help us, you can start contributing to NgRx. If you’re unsure where to start, come take a look at our new contribution guide and watch the introduction video Jan-Niklas Wortmann and Brandon Roberts have made to help you get started.
Thanks to all our contributors
NgRx continues to be a community-driven project. Design, development, documentation, and testing all are done with the help of the community. We would like to thank the contributors that helped to work towards this release of NgRx: John Crowson, Santosh Yadav, Jason Hodges, Itay Oded, Minijus L, EV, tja4472, Ismail Faizi, Alex Eagle, Serkan Sipahi, Felix Lemke, Paul King, Nacho Vazquez, Suguru Inatomi, Ferdinand Malcher, Justin Schwartzenberger, Karanveer Plaha, Koala, Marien Scott, Michael, Nils Mehlhorn, Peter B Smith, Emilio Martinez, Monsij Biswal, Alfonso Andrés López Molina, Eddybrando Vásquez, Sheik Althaf, Stas Vladykov, Stephan Renatus, ukrukarg, Dkosasih, Di Zhou, beqa7137, erhise, geraldspreer, jfrazx, musou1500, shikhin21, thefliik, Andrew Evans, Adrian Fâciu, Evgeny Fedorenko, Fabian Gosebrink, Gregor Woiwode, Felix Mohammadi Kho’i, Hermel, pegaltier, Guilherme Oenning, JiaLiPassion
If you are interested in contributing, visit our GitHub page and look through our open issues, some marked specifically for new contributors.
The createAction
and createReducer
functions are inspired by the ts-action library, created by Nicholas Jamieson.
We would also like to give a big thanks to our silver sponsor, BrieBug. BrieBug is one of the top web and mobile application development consulting firms in the state of Colorado. BrieBug provides enterprise Angular consulting, architectural reviews, team training, and staff augmentation, with multiple Angular GDEs on staff. BrieBug was previously a bronze-level sponsor, but has since increased their sponsorship of NgRx development, providing further support for development, travel, and hosting expenses.
Support NgRx
NgRx requires significant time and effort that often goes unpaid, and we would like to change that. If you or your company wants to contribute to NgRx as a backer or sponsor, please visit our OpenCollective page for different contribution options or contact us directly for other sponsorship opportunities.
Follow us on Medium and on Twitter for the latest updates about the NgRx platform.