riverpod icon indicating copy to clipboard operation
riverpod copied to clipboard

Mutation: Access mutated data

Open snapsl opened this issue 10 months ago • 11 comments

Description

Currently, riverpod gives access to the mutated state using ref.listen()/ref.watch().

@riverpod
ref.listen(todoListProvider.addTodo, (_, addTodo) {
   switch(addTodo.state) {
      ...
      _=> defaultHandle();
   }
});

Suggestion

Riverpod should enable us to access the mutated data directly, e.g. the data of a addTodo mutation would be the newly added todo. If the mutation is in a success state, the data is available via the data property.

SuccessMutationState(:final data)

snapsl avatar Jan 18 '25 08:01 snapsl

Isn't that already the case?

rrousselGit avatar Jan 18 '25 10:01 rrousselGit

If I understand it correctly, the SuccessMutationState currently holds the updated notifier.state. I suggest not restricting mutation.data to notifier.state, but rather providing access to the mutated data directly.

E.g. a addTodo mutation for a List<Todo> notifier.

Currently:

SuccessMutationState(List<Todo> value)

Proposed:

SuccessMutationState(Todo value)

snapsl avatar Jan 18 '25 10:01 snapsl

How would Riverpod know what that is?

rrousselGit avatar Jan 18 '25 11:01 rrousselGit

How would Riverpod know what that is?

Can you elaborate?


Benefits:

  1. This could align Riverpod with Tanstack implementation for mutation
  2. Direct access to mutation data
ref.listen(todoListProvider.addTodo, (_, addTodo) {
   switch(addTodo.state) {
      SuccessMutationState(value: Todo todo) => showSnackbar(content=Text("Added ${value.name}")),
      _=> null,
   }
});

snapsl avatar Jan 18 '25 11:01 snapsl

How would Riverpod know what that Todo is? It only has access to the List<Todo>. How do you get a single Todo from that?

rrousselGit avatar Jan 18 '25 11:01 rrousselGit

Hopefully I get your question correctly. 😀 From an API perspective, the mutation function could return the updated todo allowing Riverpod to access it internally.

@riverpod
class Todos {
   Future<List<Todo>> build () asnyc => ...;
   
   @mutation
   Future<Todo> addTodo(Todo value) async {
      final newTodo = client.addTodo(value);
      return newTodo;
   }
}

snapsl avatar Jan 18 '25 12:01 snapsl

@mutation Future<Todo> addTodo(Todo value) async { final newTodo = client.addTodo(value); return newTodo; }

But then Riverpod won't know how to update the state, as Todo is not assignable to List<Todo>

At minimum we'd need:

addTodo(todo) {
  final newTodo = client.addTodo(value);
  state = [...state, todo];
  return newTodo;
}

rrousselGit avatar Jan 18 '25 12:01 rrousselGit

This looks good to me. I don't see a way to not explicitly update the state.

Note: Should an error in a mutation.state automatically result in an error in notifier.state?

snapsl avatar Jan 18 '25 13:01 snapsl

That'd require changing how mutations works. But maybe

Note: Should an error in a mutation.state automatically result in an error in notifier.state?

No, I don't think that makes sense

rrousselGit avatar Jan 18 '25 13:01 rrousselGit

Note: Should an error in a mutation.state automatically result in an error in notifier.state?

No, I don't think that makes sense

Agreed. I also do not think that this should be the default, but it might be something that some users want. This could be a annotation flag.

snapsl avatar Jan 18 '25 13:01 snapsl

We can revisit that later. That's not something I'd ship in the initial release

rrousselGit avatar Jan 18 '25 14:01 rrousselGit

Mutations have changed. You should be able to do this :)

rrousselGit avatar Jun 20 '25 12:06 rrousselGit