D-James-GH / cached_query

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

Can you use await for calling invalidate & refetch in mutation? #27

Closed Jaew00Shin closed 3 months ago

Jaew00Shin commented 4 months ago
_googleLogInMutation = Mutation(
      queryFn: (accessToken) async {
        final response = await authApi.googleLogIn(accessToken: accessToken);
        validateApiResponse(response);
        return const TokenMapper()(response.result);
      },
      onSuccess: (token, _) async {
        await secureStorageDataSource.setAccessToken(token);
      },
      refetchQueries: [getUsersMe],
    );

for example, above code is my mutation for signing in with google. The goal I want is that when mutate of mutation is finished, refetching & invalidating queries should be done.

But now code is

Future<MutationState<ReturnType?>> _fetch(Arg arg) async {
    _setState(_state.copyWith(status: QueryStatus.loading));
    _emit();
    dynamic startMutationResponse;
    if (_onStartMutation != null) {
      startMutationResponse = await _onStartMutation!(arg);
    }
    // call query fn
    try {
      final res = await _queryFn(arg);
      if (_onSuccess != null) {
        await _onSuccess!(res, arg);
      }
      _setState(_state.copyWith(status: QueryStatus.success, data: res));
      if (_invalidateQueries != null) {
        for (final k in _invalidateQueries!) {
          CachedQuery.instance.invalidateCache(key: k);
        }
      }
      if (_refetchQueries != null) {
        CachedQuery.instance.refetchQueries(keys: _refetchQueries!);
      }
      return state;
    } catch (e, trace) {
      if (_onError != null) {
        await _onError!(arg, e, startMutationResponse);
      }
      _setState(
        _state.copyWith(status: QueryStatus.error, error: e),
        trace,
      );

      return state;
    } finally {
      _emit();
    }
  }
void refetchQueries({KeyFilterFunc? filterFn, List<Object>? keys}) {
    assert(
      filterFn != null || keys != null,
      "Either filterFn or keys must not be null",
    );
    if (filterFn != null) {
      final queries = _filterQueryKey(filter: filterFn);
      for (final query in queries) {
        query.refetch();
      }
    }
    if (keys != null) {
      for (final key in keys) {
        final k = encodeKey(key);
        if (_queryCache.containsKey(k)) {
          _queryCache[k]!.refetch();
        }
      }
    }
  }

Future<State> refetch();

Although method refetch in query_base.dart return future, refetchQueries in cached_query.dart return void.

So, Could you change return type of refetchQuries in cached_query.dart from void to Future. ?

I suggest the below code

Future<void> refetchQueries({KeyFilterFunc? filterFn, List<Object>? keys}) async {
    assert(
      filterFn != null || keys != null,
      "Either filterFn or keys must not be null",
    );

final queries = [_filterQueryKey(filter: filterFn), ... _queryCache.entries.where((key, query) => keys.contain(key)).map((key, query) => query)];

await Future.wait([
queries.map((query) => query.refetch())
]);

  }

This code is just example. It may not works well. But I suggest that refetchiQueries should return Future and it should have await keyword when used in _fetch method of mutation.dart.

I want to be applied this for refetchQueries as well as invalidateQueries.

The ultimate goal is that it should be insuranced refetching queries are already refetched & invalidateQueries are already invalidated after mutation is done.

D-James-GH commented 4 months ago

Just so I understand: Why do you need to await refetching the queries after a mutation? Are you running code after that requires the certain queries to be refetched first?

I think the mutation should complete once the mutation is finished as the "refetch" part is a side effect and not actually part of the mutation. However, like you say, the refetchQueries function could return a future so you could manually await it yourself. Invalidate queries is not asynchronous so it wouldn't make any sense to turn that into a future. That just sets the query to stale so that the next time it is displayed it is refetched (useful for off screen queries).

For now you could use CachedQuery.instance.whereQuery or CachedQuery.instance.getQuery to manually do your request:

Future<void> doMutation() async {
    final result = await _googleLogInMutation.mutate();
    final otherQuery = CachedQuery.instance.getQuery(otherQueryKey);
    await otherQuery?.refetch();
    // some code that relies on the refetch
}