DEV Community

Cover image for Announcing NgRx v14: Action Groups, ComponentStore Lifecycle Hooks, ESLint package, Revamped NgRx Component, and more!
Brandon Roberts for NgRx

Posted on

Announcing NgRx v14: Action Groups, ComponentStore Lifecycle Hooks, ESLint package, Revamped NgRx Component, and more!

We are pleased to announce the latest major version of the NgRx framework with some exciting new features, bug fixes, and other updates.


New Function to Create Action Groups 🧙

If you're familiar with NgRx Store, you know that actions are an important part of the application that uses it. Some might say actions are the gluten of the NgRx loaf 😉, but that's a story for another day. In NgRx 13.2, we introduced the new createActionGroup function that was initially thought of by Mike Ryan and implemented by Marko Stanimirović as a way to make creating actions even easier.

The video below gives a quick overview of writing actions separately with Good Action Hygiene™, and introduces the new createActionGroup function to minimize the amount of code to write and export groups of actions.

The createActionGroup function makes use of more advanced features that have been introduced in TypeScript, so if you're into advanced TypeScript, definitely check out the source code to find some new tricks there.


Component Store Lifecycle Hooks 🎣

The NgRx ComponentStore library has been gaining popularity because of its features to manage state reactively in Angular applications more consistently and predictably. Using ComponentStore allows you to abstract away the business logic out of your components when update state, performing side effects, and providing observable streams of state changes.

There are some use cases where you need to perform some task when the ComponentStore is created, or when the ComponentStore state is first initialized. Previously to do this, you had to expose some method on the ComponentStore and call it from the Component's lifecycle method. To simplify this, we've introduced two interfaces and lifecycle hooks for ComponentStore, OnStoreInit and OnStateInit.

OnStoreInit

The OnStoreInit interface is used the implement the ngrxOnStoreInit method in the ComponentStore class. This lifecycle method is called immediately after the ComponentStore class is instantiated.

export interface BooksState {
  collection: Book[];
}

export const initialState: BooksState = {
  collection: []
};

@Injectable()
export class BooksStore extends ComponentStore<BooksState> implements OnStoreInit {

  constructor() {
    super(initialState);
  }

  ngrxOnStoreInit() {
    // called after store has been instantiated
  }
}
Enter fullscreen mode Exit fullscreen mode

And use the provideComponentStore() function to register the ComponentStore in the providers array.

@Component({
  // ... other metadata
  providers: [
    provideComponentStore(BooksStore)
  ]
})
export class BooksPageComponent {
  constructor(private booksStore: BooksStore) {}
}
Enter fullscreen mode Exit fullscreen mode

OnStateInit

The OnStateInit interface is used the implement the ngrxOnStateInit method in the ComponentStore class. This lifecycle method is called only once after the ComponentStore state is initially set. ComponentStore supports eager and lazy initialization of state, and the lifecycle hook is called appropriately in either scenario.

Eager State Init

export interface BooksState {
  collection: Book[];
}

export const initialState: BooksState = {
  collection: []
};

@Injectable()
export class BooksStore extends ComponentStore<BooksState> implements OnStateInit {
  constructor() {
    // eager state initialization
    super(initialState);
  }

  ngrxOnStateInit() {
    // called once after state has been first initialized
  }
}
Enter fullscreen mode Exit fullscreen mode
@Component({
  // ... other metadata
  providers: [
    provideComponentStore(BooksStore)
  ]
})
export class BooksPageComponent {
  constructor(private booksStore: BooksStore) {}
}

### Lazy State Init


export interface BooksState {
  collection: Book[];
}

@Injectable()
export class BooksStore extends ComponentStore<BooksState> implements OnStateInit {
  constructor() {
    super();
  }

  ngrxOnStateInit() {
    // called once after state has been first initialized
  }
}

export const initialState: BooksState = {
  collection: []
};
Enter fullscreen mode Exit fullscreen mode
@Component({
  // ... other metadata
  providers: [
    provideComponentStore(BooksStore)
  ]
})
export class BooksPageComponent implements OnInit {
  constructor(private booksStore: BooksStore) {}

  ngOnInit() {
    // lazy state initialization
    this.booksStore.setState(initialState);
  }
}
Enter fullscreen mode Exit fullscreen mode

If you implement the lifecycle hooks in the ComponentStore, and register it with providers without using provideComponentStore(), in development mode, a warning is logged to the browser console.


Native ESLint package 📦

The NgRx team has always been looking at better ways to encourage developers to use best practices when using NgRx libraries. Team member Tim Deschryver created the ngrx-eslint-plugin package to provide ESLint rules for best practices for applications built with NgRx. In a previous version of NgRx, we introduced adding the ngrx-eslint-plugin package to your package.json, and adding lint rules to your ESLint configuration.

ng add @ngrx/store
Enter fullscreen mode Exit fullscreen mode

To provide a more integrated experience, we've moved the ngrx-eslint-plugin package into the main ngrx/platform repository, and have added a new @ngrx/eslint-plugin package for the ESLint rules. The package has already been stable for a while, and will now be more visible to developers using NgRx libraries with already available docs pages and ongoing support.


NgRx Component package overhauled ✍

The NgRx Component package has always had an ambitious goal, with that being a way to bring more reactivity to templates in Angular, along with providing support for fully zone-less Angular applications. Initially written by Michael Hladky, and ultimately turning into the @rx-angular/template library, @ngrx/component has been well received even while still being an experimental package.

NgRx Component provides two main features, the ngrxPush pipe and ngrxLet directive for more easily using observables in Angular component templates. For version 14, team member Marko Stanimirović has completely rewritten the component package from the ground up to provide a more consistent way to handle observable and static data, improved performance for zone-less applications in Angular, and more.

NgRx Component also introduces two new modules: LetModule and PushModule.

To use the new modules, import and add the LetModule or PushModule to the imports array in your standalone component or NgModule.

import { Component } from '@angular/core';
import { LetModule, PushModule } from '@ngrx/component';

@Component({
  // ... other metadata
  standalone: true,
  imports: [
    // ... other imports
    LetModule,
    PushModule
  ],
})
export class MyStandaloneComponent {}
Enter fullscreen mode Exit fullscreen mode

These new NgModules allow for more granular usage of the ngrxPush pipe and ngrxLet directive, and paves the way to introduce standalone versions of these pipes and directives to take more advantage of the new standalone features introduced in Angular v14. Read this post to learn more about the NgRx Component updates in v14.


The future of NgRx in Angular 🌞

Angular v14 has introduced some exciting new features for components, pipes, and directives, a new "environment injector", and an updated inject() function for using Dependency Injection without directly using a constructor. The standalone features for components, pipes, and directives allow you to build Angular applications with NgModules primarily being optional.

As most NgRx libraries primarily are sets of providers, and don't provide UI components, this allows us to take a fresh look with these new standalone APIs to register NgRx providers in Angular applications without NgModules. Check out Marko's latest blog post on using NgRx in Angular with standalone features. The inject() function has been around for a while, but now provides expanded capabilities for composing functions that have access to inject dependencies without the constructor, and easier composition of extended classes.

While Angular standalone features are still in developer preview, we have proposed some new APIs for NgRx in our latest RFC. If you would like to have some input on future Angular applications built with NgRx, feel free to check out the RFC and share your thoughts.


Swag store and Discord Server! 👕

You can get official NgRx swag through our store! T-shirts with the NgRx logo are available in many different sizes, materials, and colors. We will look at adding new items to the store such as stickers, magnets, and more in the future. Visit our store to get your NgRx swag today!

Join our discord server for those who want to engage with other members of the NgRx community, old and new.


Deprecations and Breaking Changes 💥

This release contains bug fixes, deprecations, and breaking changes. For most of these deprecations or breaking changes, we've provided a migration that automatically runs when you upgrade your application to the latest version.

Take a look at the version 14 migration guide for complete information regarding migrating to the latest release. The complete CHANGELOG can be found in our GitHub repository.


Upgrading to NgRx 14 🗓️

To start using NgRx 14, make sure to have the following minimum versions installed:

  • Angular version 14.x
  • Angular CLI version 14.x
  • TypeScript version 4.7.x
  • RxJS version ^6.4.x or RxJS version ^7.5.x

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
Enter fullscreen mode Exit fullscreen mode

Contributing to NgRx 🥰

We're always trying to improve the docs and keep them up-to-date for users of the NgRx framework. To help us, you can start contributing to NgRx. If you're unsure where to start, come take a look at our 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 and sponsors

NgRx continues to be a community-driven project. Design, development, documentation, and testing all are done with the help of the community. We've recently added a community section to the NgRx team page that lists every person that has contributed to the platform.

If you are interested in contributing, visit our GitHub page and look through our open issues, some marked specifically for new contributors. We also have active GitHub discussions for new features and enhancements.


Sponsor NgRx 💲

We are looking for our next Gold sponsor, so if your company wants to sponsor the continued development of NgRx, please visit our OpenCollective page for different sponsorship options, or contact us directly to discuss other sponsorship opportunities.

We want to give a big thanks to our Silver sponsor, Narhwal Technologies! Nrwl has been a longtime promoter of NgRx as a tool for building large Angular applications, and is committed to supporting open source projects that they rely on for Nx.

Follow us on Twitter for the latest updates about the NgRx platform.

Discussion (0)