Logo

dev-resources.site

for different kinds of informations.

Announcing NgRx 19: NgRx Signals Features, Action Signals, and more!

Published at
12/19/2024
Categories
angular
ngrx
Author
brandontroberts
Categories
2 categories in total
angular
open
ngrx
open
Author
15 person written this
brandontroberts
open
Announcing NgRx 19: NgRx Signals Features, Action Signals, 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.


Updated NgRx Signals Features🚦

The NgRx Signals was released as stable in v18 as a new library built from the ground up with Angular Signals, opt-in RxJS interoperability, and entity management out of the box. the NgRx Signals library has continued to receive quality updates and improvements to enhance the developer experience.

New Props SignalStore Feature

One highly requested feature was the ability to define props for a SignalStore during its creation. To support this, the withProps base feature was added to allow static properties or observables to be defined as SignalStore members.

export const BooksStore = signalStore(
  withEntities<Book>(),
  withRequestStatus(),
  withProps(({ isFulfilled }) => ({
    fulfilled$: toObservable(isFulfilled).pipe(filter(Boolean)),
  })),
);
Enter fullscreen mode Exit fullscreen mode

It can also be used for defining all dependencies in a single place:

export const MyStore = signalStore(
  withProps(() => ({
    service1: inject(Service1),
    service2: inject(Service2),
  })),
  withMethods(({ service1, service2 }) => ({
    method1() {
      service1.foo();
    },
    method2() {
      service2.bar();
    },
  }))
);
Enter fullscreen mode Exit fullscreen mode

The new withProps feature integrates together with new Angular Signals features including resource and linkedSignal:

const booksStore = signalStore(
  withProps(() => {
    const bookResource = resource({
      loader: () => Promise.resolve({ id: 1, title: "'Lord of the Rings' }),"
    });
    return {
      _bookResource: bookResource,
      bookResource: bookResource.asReadonly(),
    };
  }),
);
Enter fullscreen mode Exit fullscreen mode

The above exposes bookResource as readonly resource, and keeps the writable resource.

The next example exposes prettyTitle as linkedSignal, and keeps the writable state internally.

signalStore(
  withState({ id: 1, title: "'Lord of the Rings' }),"
  withProps((store) => ({
    prettyTitle: linkedSignal(() => \`${store.id()}: ${state.store()}\`),
  })),
);
Enter fullscreen mode Exit fullscreen mode

The new withProps features allows you customize and extend the SignalStore even further with Angular Signals. Thanks to Marko Stanimirović for his continued work on improving NgRx Signals!

Signal Method Utility Function

The rxMethod utility function was introduced to provide a way to connect async work done with RxJS to Angular Signals. We've also introduced the signalMethod utility function that gives you the same benefits of rxMethod with the ability to use only use signals.

signalMethod is a factory function that processes side effects on signals or static values.

import { Component } from '@angular/core';
import { signalMethod } from '@ngrx/signals';

@Component({ /* ... */ })
export class NumbersComponent {
  // πŸ‘‡ This method will have an input argument
  // of type `number | Signal<number>`.
  readonly logDoubledNumber = signalMethod<number>((num) => {
    const double = num * 2;
    console.log(double);
  });
}
Enter fullscreen mode Exit fullscreen mode

At first sight, signalMethod, might look the same as an effect. However, signalMethod offers three distinctive advantages over effect:

@Component({ /* ... */ })
export class NumbersComponent {
  readonly num = signal(2);
  readonly logDoubledNumberEffect = effect(() => {
    console.log(this.num() * 2);
  });
  readonly logDoubledNumber = signalMethod<number>((num) => {
    console.log(num * 2);
  });

  constructor() {
    this.logDoubledNumber(this.num);
  }
}
Enter fullscreen mode Exit fullscreen mode

Flexible Input: The input argument can be a static value, not just a signal. Additionally, the processor function can be called multiple times with different inputs.
No Injection Context Required: Unlike an effect, that requires an injection context or an Injector, signalMethod's "processor function" can be called without an injection context.
Explicit Tracking: Only the Signal of the parameter is tracked, while Signals within the "processor function" stay untracked.

Read the docs to learn more about signalMethod.

Development Mode Checks for State Mutations

Ensuring that state updates are immutable is crucial for Signal to emit correctly and trigger necessary DOM updates, derived signals, or side effects. To mitigate this issue the patchState function applies a deep freeze on the state in development mode.

Before:

const userState = signalState(initialState);
patchState(userState, (state) => {
  state.user.firstName = 'mutable change'; // mutable change which went through
  return state;
});
Enter fullscreen mode Exit fullscreen mode

After:

const userState = signalState(initialState);
patchState(userState, (state) => {
  state.user.firstName = 'mutable change'; // throws in dev mode
  return state;
});
Enter fullscreen mode Exit fullscreen mode

This ensures that state changes remain immutable, providing a path to retaining performance and best practices.

NgRx Store Support for Dispatching Actions on Signal changes

NgRx Store remains the defacto choice for global state management within Angular applications. NgRx Store previously introduced support for selecting state as a signal, and now introduces an ergonomic way to dispatch actions that read signals.

class BookComponent {
  bookId = input.required<number>();

  constructor(store: Store) {
    store.dispatch(() => loadBook({ id: this.bookId() })));
  }
}
Enter fullscreen mode Exit fullscreen mode

The dispatch method executes initially and every time the bookId changes. If dispatch is called within an injection context, the signal is tracked until the context is destroyed. In the example above, that would be when BookComponent is destroyed.

When dispatch is called outside a component's injection context, the signal is tracked globally throughout the application's lifecycle. To ensure proper cleanup in such a case, provide the component's injector to the dispatch method:

class BookComponent {  
  bookId = input.required<number>();
  injector = inject(Injector);
  store = inject(Store);

  ngOnInit() {
    // runs outside the injection context
    this.store.dispatch(
      () => loadBook({ id: this.bookId() }),
      { injector: this.injector }
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Thanks to Rainer Hahnekamp for adding these features!

NgRx Signals, the new Default 🀝

NgRx Signals is a ground-up approach to managing state reactively, and is opt-in for RxJS usage. It also has other utilities for working with Angular Signals in a structured way that helps developers scale up. NgRx Signals is now the recommended local state management library to integrate into your Angular applications. For new applications, start with NgRx SignalStore. For existing applications, consider migrating to the new @ngrx/signals package.

NgRx Workshops πŸŽ“

With NgRx usage continuing to grow with Angular, many developers and teams still need guidance on how to architect and build enterprise-grade Angular applications. We are excited to introduce upcoming workshops provided directly by the NgRx team!

Starting in Feburary, we will offer one to three full-day workshops that cover the basics of NgRx to the most advanced topics. Whether your teams are just starting with NgRx or have been using it for a while - they are guaranteed to learn new concepts during these workshops.

The workshop covers both global state with NgRx Store and libraries, along with managing local state with NgRx ComponentStore and NgRx Signals.

Visit our workshops page to sign up from our list of upcoming workshops.

New Docs site πŸ’…

The new docs revamp is underway and is built up from scratch using AnalogJS and will have a brand-new design and revamped content. This will make sure to offer the best possible experience to our users and make it easier to find the information you need. It also ensures that the website will become easier to maintain and update. Because we're a community-driven project, we're keeping in mind that the website should be easy to contribute to.

Thanks to Mike Ryan for his work on the new version of the NgRx website.


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 19 migration guide for complete information regarding migrating to the latest release. The complete CHANGELOG can be found in our GitHub repository.


Upgrading to NgRx 19 πŸ—“οΈ

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

  • Angular version 19.x
  • Angular CLI version 19.x
  • TypeScript version 5.5.x
  • RxJS version ^6.5.x or ^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@19
Enter fullscreen mode Exit fullscreen mode

If your project uses @ngrx/signals, run the following command:

ng update @ngrx/signals@19
Enter fullscreen mode Exit fullscreen mode

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.


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. Visit our community contributors section to see every person who has contributed to the framework.

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.

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

We also want to thank our Bronze sponsor, House of Angular!

Follow us on Bluesky, LinkedIn, and Twitter for the latest updates about the NgRx platform.

ngrx Article's
30 articles in total
Favicon
πŸš€ Learning Through Experience: A Tale of NgRx Effects
Favicon
Announcing NgRx 19: NgRx Signals Features, Action Signals, and more!
Favicon
NGRX with Angular 16
Favicon
Angular Addicts #31: The new Resource API, effect updates & more
Favicon
Simplify Your Angular Code with NgRx Entities
Favicon
ngRx Store in Angular
Favicon
Angular Addicts #29: Angular 18.2, implicit libraries, the future is standalone & more
Favicon
NgRx Use Cases, Part III: Decision-making
Favicon
Angular Addicts #28: Angular 18.1 (w. the new @let syntax), Component testing, SSR guide & more
Favicon
Angular Router URL Parameters Using NgRx Router Store
Favicon
When to Use `concatMap`, `mergeMap`, `switchMap`, and `exhaustMap` Operators in Building a CRUD with NgRx
Favicon
How to Implement ActionCreationGroup in NgRx
Favicon
A single state for Loading/Success/Error in NgRx
Favicon
How to Debug NgRx Using REDUX DevTools in Angular
Favicon
When and Why to Use REDUX NgRx in Angular
Favicon
Announcing NgRx Signals v18: State Encapsulation, Private Store Members, Enhanced Entity Management, and more!
Favicon
Angular Addicts #27: NgRx 18, New RFC: DomRef API, Web storage with Signals & more
Favicon
Announcing NgRx 18: NgRx Signals is almost stable, ESLint v9 Support, New Logo and Redesign, Workshops, and more!
Favicon
Angular Addicts #26: Angular 18, best practices, recent conference recordings & more
Favicon
How to Handle Side Effects in Angular Using NgRx Effects
Favicon
How to Use NgRx Selectors in Angular
Favicon
Angular Addicts #25: Angular and Wiz will be merged, the differences between React and Angular & more
Favicon
Angular Addicts #24: Angular 17.3, Signals and unit testing best practices, Storybook 8 & more
Favicon
Creating a ToDo App with Angular, NestJS, and NgRx in a Nx Monorepo
Favicon
Here's how NgRx selectors actually work internally
Favicon
Angular Addicts #23: Angular 17.2, Nx 18, Signal forms, Analog, WebExpo & more
Favicon
All you need to know to get started with the NgRx Signal Store
Favicon
Angular Addicts #22: Angular 17.1, Signal Inputs, State management tips & more
Favicon
11 friends of state management in Angular
Favicon
The best of Angular: a collection of my favorite resources of 2023

Featured ones: