apollo
apollo copied to clipboard
`usequery()` `result` set to undefined while refetching after updating variable
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
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)
})
}
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.
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.