D-James-GH / cached_query

Simple caching for flutter apps
MIT License
47 stars 10 forks source link

Feature Request: Support Array-Based Keys for Queries and Mutations #41

Closed xeinebiu closed 1 week ago

xeinebiu commented 1 week ago

Hi,

Currently, the Key for a Query or Mutation can only be a String. I would like to request adding support for array-based keys, similar to how it works in Tanstack's ReactQuery.

What This Feature Would Do

Allow the Query and Mutation objects to use array-based keys. This would make it easier to invalidate data in a more controlled way.

Example Scenario

Imagine you have multiple queries like this:

// Using an array-based key
final queryKey = ["get-users", "1", "notes", 5];

// Query with an array-based key
final userNotesQuery = Query(
  key: queryKey,
  queryFn: () async {
    // Your query function
  },
);

// Mutation with an array-based key
final userMutation = Mutation(
  key: ["update-user", "1"],
  mutationFn: (data) async {
    // Your mutation function
  },
);

Invalidation Examples

  1. Invalidate all get-users queries:

    cache.invalidateQueries(["get-users"]);

    This will invalidate:

    • ["get-users"]
    • ["get-users", "1"]
    • ["get-users", "1", "notes", 5]
  2. Invalidate a specific user query:

    cache.invalidateQueries(["get-users", "1"]);

    This will invalidate:

    • ["get-users", "1"]
    • ["get-users", "1", "notes", 5]

    But it won’t affect other users' queries.

  3. Invalidate a specific note of a user:

    cache.invalidateQueries(["get-users", "1", "notes", 5]);

    This will only invalidate the query for that specific note.

Why This is Useful

Thank you!

D-James-GH commented 1 week ago

Hi @xeinebiu, The key to a query is dynamically typed and can be any json-serializable object. However, you are correct in that it does just get converted to a string and stored.

I have thought about implementing the react-query way of doing the keys and while useful I'm not convinced the complexity of the implementation and learning out weighs the use cases for it.

The main time I have used the keyed logic in react has been, like you say, to invalidate or operate on multiple grouped queries at the same time. In CachedQuery there are a few of ways you can get essentially the same behaviour, lets use the example you provided. In this example the key is perfectly valid.

final queryKey = ["get-users", "1", "notes", 5];

final userNotesQuery = Query(
  key: queryKey,
  queryFn: () async {
    // Your query function
  },
);

Let me know if you have any questions.

xeinebiu commented 1 week ago

Hi @xeinebiu, The key to a query is dynamically typed and can be any json-serializable object. However, you are correct in that it does just get converted to a string and stored.

I have thought about implementing the react-query way of doing the keys and while useful I'm not convinced the complexity of the implementation and learning out weighs the use cases for it.

The main time I have used the keyed logic in react has been, like you say, to invalidate or operate on multiple grouped queries at the same time. In CachedQuery there are a few of ways you can get essentially the same behaviour, lets use the example you provided. In this example the key is perfectly valid.

final queryKey = ["get-users", "1", "notes", 5];

final userNotesQuery = Query(
  key: queryKey,
  queryFn: () async {
    // Your query function
  },
);
* Option 1 - simply find the query and refetch
// The unencodedKey contains the original object that was passed in.
// Use Dart's pattern matching to filter queries.
CachedQuery.instance
    .whereQuery((query) =>
        switch (query.unencodedKey) { ["get-users", ...] => true, _ => false })
    ?.forEach((q) => q.refetch());
* Option 2 - Use the `filterFn` on the `refetchQueries` method:
CachedQuery.instance.refetchQueries(
  filterFn: (unencodedKey, key) {
    return switch (unencodedKey) { ["get-users", ...] => true, _ => false };
  },
);
* Option 3 - Use a hierarchical string that matches the url, similar to React SWR
final userNoteKey = (userId, noteId) => "get-users/$userId/notes/$noteId";
CachedQuery.instance.refetchQueries(
  filterFn: (unencodedKey, key) => key.startsWith("get-users/12"),
);

Let me know if you have any questions.

Thanls for the quick reply! Everything looks good so far, and the options you shared seem useful.