Skip to content

Conversation

manfredsteyer
Copy link
Collaborator

rxMutation creates a mutation function in the store, delegating to a function/ method returning an observable, and takes care of race conditions by internally using mergeMap (default), switchMap, concatMap, or exhaustMap.

mutation also creates an async mutation function, delegating to a function/ method returning an observable. When the async function is awaited without throwing an exception, the caller can safely assume that it has been executed. However, here, the consumer is responsible for preventing race conditions.

Copy link
Collaborator

@rainerhahnekamp rainerhahnekamp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review consists of two parts. Things that should be addressed before we merge it, and things that can be done later, as additional features.

I've added a TODO prefix to all my comments that fall into the second category (which could be done later).

Here's a summary, where we could have separate Issues and PR contributions.

  • #220 rxMutate as fully standalone function which would also work outside the SignalStore
  • Documentation in Docusaurus
  • Refactoring of Tests (depends on standalone rxMutation, we can do more directly in that file and don’t have to go through the complete SignalStore.
  • Allow withMutations to expose more tracking data of the mutation, like isPending, hasValue.
  • E2E Tests for Mutation Demo
  • Standalone and Promise-based mutation
  • #221 Standalone httpMutation in the same manner as httpResource.
  • Redesign of flatten operator: Should probably have a callback telling that a certain input has been skipped.

Copy link
Collaborator

@rainerhahnekamp rainerhahnekamp left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good to go. Well done Mr @manfredsteyer

@rainerhahnekamp rainerhahnekamp merged commit 7dbf391 into main Aug 31, 2025
1 check passed
rainerhahnekamp pushed a commit that referenced this pull request Aug 31, 2025
This introduces `withMutations` as a new SignalStore feature,
with initial support for RxJS-powered mutations via `rxMutation`.

Example:
```typescript
    export const CounterStore = signalStore(
      withState({ counter: 0 }),
      withMutations((store) => ({
        increment: rxMutation({
          operation: (value: number) => {
            return httpClient.post('counter/increase', { value });
          },
          onSuccess: (result) => {
            patchState(store, { counter: result });
          },
        }),
      })),
    );

    const counterStore = inject(CounterStore);
    counterStore.increment(1);
    console.log(counterStore.counter()); // prints 1
````

---

- Mutations is the term the Angular team uses for a potential upcoming
  feature that complements *resources* (which are read-only). Mutations
  enable writing data back to the server.
- To align with this mutation-based mindset, we are introducing custom
  mutation support in the toolkit.
- The design is inspired by @markostanimirovic’s prototype:
  https://github.com/markostanimirovic/rx-resource-proto
- Thanks to Marko for consulting with the NgRx team, and to Alex
  Rickabaugh (@alxhub) for the core mutation design.
- We also took ideas from TanStack Query's mutation feature:
  https://tanstack.com/query/latest

---

This commit lays the foundation for further expansion of mutation
support in SignalStore.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants