apollo icon indicating copy to clipboard operation
apollo copied to clipboard

`usequery()` `result` set to undefined while refetching after updating variable

Open andgith opened this issue 1 year ago • 2 comments

Describe the bug The value of result is undefined while a refetch occurs after a variable update.

const variables = ref({
  id: props.id,
  sort: sort.value,
  group: group.value,
});

const { result, error } = useQuery(searchQuery, () => variables);

Expected behavior In versions before beta-2 did not set result to undefined during a refetch.

Versions vue: 3.3.4 vue-apollo: ^4.0.0-beta.1 @apollo/client: 3.7.15

andgith avatar Jun 15 '23 21:06 andgith

Seems like the culprit is this line: https://github.com/vuejs/apollo/blob/v4/packages/vue-apollo-composable/src/useQuery.ts#L325

This method gets called twice when variables change due to the query being restarted. For the first call, "data" is not present as the query is still loading so variables get cleared. For the send call, "data" will be set (for a successful request that is) and the "result" will have a value again.

Proposed solution:

function processNextResult (queryResult: ApolloQueryResult<TResult>) {
    result.value = queryResult.data && Object.keys(queryResult.data).length === 0 ? result.value : queryResult.data
    // Old: result.value = queryResult.data && Object.keys(queryResult.data).length === 0 ? undefined : queryResult.data
    loading.value = queryResult.loading
    networkStatus.value = queryResult.networkStatus
    // Wait for handlers to be registered
    nextTick(() => {
      resultEvent.trigger(queryResult)
    })
  }

Csszabi98 avatar Jul 06 '23 16:07 Csszabi98

A simple workaround:

const useStableQueryResult = <TResult, TVariables extends OperationVariables>({
  result,
}: Pick<UseQueryReturn<TResult, TVariables>, 'result'>) => {
  const stableResult = ref(result.value);

  watch(result, (newResult) => {
    if (newResult) {
      stableResult.value = newResult as UnwrapRef<TResult>;
    }
  });

  return stableResult;
};

You can also make this the default behavior of useQuery via a wrapper, or just use it directly with your query.

Csszabi98 avatar Jul 06 '23 16:07 Csszabi98

A simple workaround:

const useStableQueryResult = <TResult, TVariables extends OperationVariables>({
  result,
}: Pick<UseQueryReturn<TResult, TVariables>, 'result'>) => {
  const stableResult = ref(result.value);

  watch(result, (newResult) => {
    if (newResult) {
      stableResult.value = newResult as UnwrapRef<TResult>;
    }
  });

  return stableResult;
};

You can also make this the default behavior of useQuery via a wrapper, or just use it directly with your query.

If a query returns GraphQL errors - {"errors": []}, then a result will be the same as the previous query. And if a developer doesn't handle an errors using onError, then an user won't understand that the result doesn't match the new query.

demershov avatar Aug 15 '24 13:08 demershov