0

I'm using the urql GraphQL-Client to run a bunch of queries in a component. I want to create a loading computed prop indicating if any of these queries are running. To be able to easily compute this, I wrapped the queries in an object:

const gql = {
  queryA: doQueryA(),
  queryB: doQueryB(),
  queryC: doQueryC()
};

which should allow me to compute the loading state like:

// does not work
const loading = computed(() => Object.values(gql).some(query => query.fetching));

however, this does not update once all queries are done. Doing it explicitly works:

// works
const loading = computed(() =>
  gql.queryA.fetching ||
  gql.queryB.fetching ||
  gql.queryC.fetching
);

which made me think that since .some can return early, maybe that's a problem for Vues reactivity tracking. So I tried to force it to access every query every time:

// does not work
const loading = computed(() => Object.values(gql)
  .map(query => query.fetching)
  .some(isFetching => isFetching));

However, that does not help either.

Can somebody explain why this problem exists?

NoBullsh1t
  • 483
  • 4
  • 14

2 Answers2

0

The below snippet and words are form Vue documentation here

Consider,

const author = reactive({
 name: 'John Doe',
 books: [
   'Vue 2 - Advanced Guide',
   'Vue 3 - Basic Guide',
   'Vue 4 - The Mystery'
 ]
})
// in component
function calculateBooksMessage() {
  return author.books.length > 0 ? 'Yes' : 'No'
}

A computed property will only re-evaluate when some of its reactive dependencies have changed. This means as long as author.books has not changed, multiple access to publishedBooksMessage will immediately return the previously computed result without having to run the getter function again.

This also means the following computed property will never update, because Date.now() is not a reactive dependency:

const now = computed(() => Date.now())

So, for your question, since gql is not a reactive property, it does not fire update for the computed property.

Kiran Parajuli
  • 820
  • 6
  • 14
  • I understand the reactivity fundamentals. Mostly, at least. I'm fairly sure that the `fetching` property is reactive. Otherwise, how could the more explicit approach I showed work? And I do access `fetching` directly - after having gotten the parent object out of the `values()` array. If this wasn't working at all, I'd suspect a basic reactivity issue. But it's only not working when I access it in a particular way, which I find confusing – NoBullsh1t Aug 14 '23 at 11:32
0

Urql's .fetching is a ref<boolean>. That means that the code needs to be adjusted to

const loading = computed(() => Object.values(gql)
  .map(query => query.fetching)
  .some(isFetching => isFetching.value)); // add .value access

otherwise the reference that is isFetching will always be truthy.

Then how did the other example work? Well, it all comes down to the fact that "" || 7 is not true but 7. So ref(false) || ref(true) will just be ref(false). Effectively, my loading property was just tracking the first of all the queries, which gave the impression that it was at least doing something. Not the right thing, though.

NoBullsh1t
  • 483
  • 4
  • 14