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.

An image comparing both ways to create 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.

An image comparing both ways to create reducers

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 mutated
  • strictActionImmutability: to verify that actions aren’t mutated
  • strictStateSerializability: to verify if the state is serializable
  • strictActionSerializability: 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>.

An image comparing both ways to create effects

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 to USER_PROVIDED_META_REDUCERS. The META_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 and createSelector functions are now a MemoizedSelector instead of a Selector
  • The ngrx-store-freeze package has been deprecated in favor of runtime checks

@ngrx/router-store

  • Usage of forRoot is now required for StoreRouterConnectingModule

@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 to actionsSafelist
  • actionsBlacklist is renamed to actionsBlocklist

@ngrx/data

  • NgrxDataModule is renamed to EntityDataModule
  • NgrxDataModuleWithoutEffects is renamed to EntityDataModuleWithoutEffects
  • NgrxDataModuleConfig is renamed to EntityDataModuleConfig

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.