vuejs / apollo

🚀 Apollo/GraphQL integration for VueJS
http://apollo.vuejs.org
MIT License
6.02k stars 523 forks source link

onResult hook called while query still in flight #1482

Open andgith opened 1 year ago

andgith commented 1 year ago

Describe the bug When using onResult the hook is called while the query is still loading and contains no data. This is a change from previous behaviour, is this intended?

To Reproduce Add onResult hook to useQuery and log output:

onResult((queryResult) => { console.log(queryResult); });

Versions vue: 3.3.4 vue-apollo: 4.0.0-beta.7 @apollo/client: 3.4.13

image
websitevirtuoso commented 1 year ago

yes I have the same issue in latest 2 versions

websitevirtuoso commented 1 year ago

To fix it I have to write if to check result

onResult((queryResult) => {
if(queryResult.data !== undefined){
console.log(queryResult);
}
});
websitevirtuoso commented 1 year ago

image

const { onResult } = useQuery(GetPosts, { filter: { id: [route.params.id] } }, { clientId: 'public' })
const { mutate, loading, onDone, onError } = useMutation(PostUpsert)

onResult((queryResult) => {
  console.log('queryResult')
  console.log(queryResult)
  redirectNotFoundIfEmpty(queryResult.data.posts.data)
  ;({
    id: initialValues.id,
    title: initialValues.title,
    slug: initialValues.slug,
    status: initialValues.status,
    content: initialValues.content,
    category: { id: initialValues.category_id },
    meta_title: initialValues.meta_title,
    meta_keyword: initialValues.meta_keyword,
    meta_description: initialValues.meta_description,
  } = queryResult.data.posts.data[0])

  mediaItems.value = JSON.parse(JSON.stringify(queryResult.data.posts.data[0]))
})

Hope it can be fixed in repo

websitevirtuoso commented 1 year ago

anyone can check it please?

websitevirtuoso commented 1 year ago

According tests in repo

<script lang="ts" setup>
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'
import { ref } from 'vue'

const { onResult, loading } = useQuery(gql`
  query channel ($id: ID!) {
    channel (id: $id) {
      id
      label
      messages {
        id
        text
      }
    }
  }
`, {
  id: 'general',
})

const channel = ref<any>(null)

onResult((result) => {
  channel.value = result.data?.channel
})
</script>

<template>
  <div class="m-6 border border-green-500 rounded">
    <div
      v-if="loading"
      class="loading"
    >
      Loading...
    </div>

    <div
      v-if="channel"
      data-test-id="data"
    >
      <div>Loaded channel: {{ channel.label }}</div>
      <div>Messages: {{ channel.messages.length }}</div>
    </div>
  </div>
</template>

We should get result when it will be available. channel.value = result.data?.channel I tried to find working version to point or even made a PR for fix. but I failed and repo a bit complex in structure for me.

lwpinion commented 11 months ago

Recently upgraded to the latest version and started having some weird problems. Only once I started looking around on the open issues did I discover this. Are there any planned changes/fixes for this? What is the best workaround?

websitevirtuoso commented 11 months ago

This is not a bug. IN my case this is proper behaviour. I have do always check if query in flight

const { onResult } = useQuery(GeMe, {}, () => ({ enabled: userState.authorized }))

onResult(({ data }) => {
  if (data === undefined) return
  userState.setUser(data.me)
  setPermissions(parseUserPermissions(data.me))
  userState.favorites = data.me.favorites.map((item: Favorite) => item.id)
})

Explaining why this is not bug. let's consider next query

#import "../fragments/listing.fragment.gql"
#import "../fragments/listingType.fragment.gql"
#import "../fragments/listingMedia.fragment.gql"

query GetListings($pagination: Pagination, $sort: Sortable, $filter: ListingFilter, $mediaOnlyPrimary: Boolean) {
  listings(pagination: $pagination, sort: $sort, filter: $filter) {
    data {
      ...ListingFragment
      type {
        ...ListingTypeFragment
      }
      media(filter: { is_primary: $mediaOnlyPrimary }) {
        ...MediaFragment
      }
      user {
        id
        first_name
        last_name
      }
    }
    total
    hasPages
    lastPage
    hasMorePages
  }
}

ON my vuejs I have pagination and I want don't reset parameters:

    total
    hasPages
    lastPage
    hasMorePages

So to do this I have next condition to empty key "data" but keep all other values to keep elements in place and in correct condition.

onResult(({ data }) => {
  // when we do preflight need to keep all values except items
  if (data === undefined) {
    listings.value = { ...listings.value, data: [] }
    return
  }

  listings.value = data.listings
})

I hope this will help everyone who had missunderstanding

qwertyuiopngsdfg commented 11 months ago

@websitevirtuoso thank you. Excuse me for asking. I understand that it is not a bug, but as type of "data" is not registered as a possibly undefined. Do you have any plans to resolve this issue?

image
websitevirtuoso commented 11 months ago

No I don't have any plans to solve it. If you don't like this behaviour you can write wrapper around result and skip preflight data and get data only when it will be available

Akryum commented 9 months ago

@qwertyuiopngsdfg It looks like it's an issue in Apollo Client:

https://github.com/apollographql/apollo-client/blob/9b22974b3e0f131a50c2618cc08ea6745e79a653/src/core/types.ts#L143

lermontex commented 7 months ago

@Akryum the same issue with @vue/apollo-composable 4.0.1 and @apollo/client 3.9.5

The error disappears if you set fetchPolicy: "no-cache", perhaps this can somehow help understand the reason for this behavior?