Skip to content

Add mutationSignal and MutationState #440

@GiancarloCante

Description

@GiancarloCante

Currently, for async actions that require a manual trigger (for example, createPost), the approach shown in the examples is to use an asyncSignal initialized with .data(null) to represent the “idle” state.

final post = asyncSignal<Post?>(AsyncState.data(null));
final postAddOrEdit = asyncSignal<Post?>(AsyncState.data(null));
final delete = asyncSignal<void>(AsyncState.data(null));


final createPost = asyncSignal<Post?>(AsyncState.data(null));

When used this way, the state is typically handled like this:

switch (createPost.value) {
  AsyncData<Post?> data => switch (data.value) {
      null => 'idle',
      Post() => 'success',
    },
  AsyncError() => 'error',
  AsyncLoading() => 'loading',
}

While this works, the intent of the code is not very clear. Using null to represent the idle state is implicit and makes the state model harder to reason about and communicate.


Proposed Solution

In riverpod, there is a dedicated MutationState abstraction designed specifically for this use case.

The idea is to introduce a mutationSignal that models mutation lifecycles explicitly.

final createPost = mutationSignal<Post>(); // Idle by default

This would allow consumers to handle states in a much clearer and more expressive way:

switch (createPost.value) {
  MutationIdle() => 'idle',
  MutationSuccess() => 'success',
  MutationError() => 'error',
  MutationPending() => 'pending',
}

This approach removes the need for sentinel values like null, improves readability, and makes mutation intent explicit and self-documenting.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions