Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(vue) - Fix Vue Suspense by making thenable reactive #1159

Merged
merged 2 commits into from
Nov 16, 2020

Conversation

kitten
Copy link
Member

@kitten kitten commented Nov 16, 2020

Summary

This should fix the thenable in useQuery's Vue Suspense implementation by making it more reactive.

cc @LinusBorg

@changeset-bot
Copy link

changeset-bot bot commented Nov 16, 2020

🦋 Changeset detected

Latest commit: 4cf83ba

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@urql/vue Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Copy link
Collaborator

@JoviDeCroock JoviDeCroock left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Neat :shipit:

Copy link
Contributor

@LinusBorg LinusBorg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a great start and definitely an improvement. But I think we can do better and refactor those multiple specific watch effects into something less verbose.

When I find some time tomorrow I'll take a stab at it, provided this is merged by then.

() => {
const newRequest = createRequest<T, V>(args.query, args.variables as any);
if (request.value.key !== newRequest.key) {
request.value = newRequest;
}
},
{
immediate: true,
flush: 'sync',
Copy link
Contributor

@LinusBorg LinusBorg Nov 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sync flushing is problematic and usually not needed. In this case, it could lead to unnecessary request re-runs:

const query = ref(`...`)
const variables = ref({ foo: 'bar' })
const result = useQuery({ query, variables })

query.value = `new query`
variables.value = { foo: 'baz' }

The above will create a new query 3 times, where 2 would be enough:

  1. the initial request from calling useQuery
  2. user changes query ref -> new request created synchronously
  3. user changes variable ref on the very next line - > new request created again, immediately replacing the request that was just created.

the default flush behavior (pre) should work fine here.

const unsubscribe: Ref<() => void> = ref(noop);

watch(
[args.query, args.variables],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is problematic, as it only watches for shallow changes of the ref.

A user could do this:

const variables = ref({
   foo: 'bar'
})

const query = useQuery({ query, variables })

variables.value.foo = 'baz'

and would expect for the effect to re-run, but it won't. As an immediate remedy, we can use the deep: true option. But that's not perfect either - in cases where they query isn't a string but an actual gql object, the whole object would be traversed needlessly in order to deep watch for changes.

I think we can refactor from these individual property watches, but I'll write that up later.

},
{
immediate: true,
flush: 'sync',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as above: sync behavior is problematic and should be removed.

[source],
(_, __, onInvalidate) => {
if (!source.value) {
return unsubscribe.value();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe I'm missing something, but the onInvalidate callback should already take care of unsubscribing in either case. we can just return here without unsubscribing again.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is here because of pausing. executeQuery can update source.value and the watcher may set it to undefined when everything's paused, so that executeQuery can explicitly take precedence over the watcher's subscription being paused.

let result$: Promise<UseQueryState<T, V>>;
if (fetching.value && source.value) {
result$ = pipe(
source.value,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disclaimer: This might be my lack of understanding for wonka speaking:

What happens if, while the request of the first source is still running, a side-effect i.e. changes variables and a new source & request is created?

  • Would this promise never resolve?
  • Would this promise still resolve to the original requests value?
  • Would this promise somehow resolve to the second request's value, as we concat sources on line 134?

The third behavior is what I think should happen but I can't tell if it does, and the tests don't cover it yet.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the second case right now, but potentially we can now try shoehorning the third case in.

@kitten
Copy link
Member Author

kitten commented Nov 16, 2020

Awesome! Generally I made the watchers more verbose as I found it's easier to make it work this way first and I'm sure we can trim it back down later ✌️

Good shout on the promise behaviour. I'm not sure if we do want to support a case where the source is a moving target as that could maybe lead to some gnarly looping behaviour when user code goes wrong, but generally I agree.

We can try to do that, yea

Edit; merging this but I expect we'll improve on this in separate PRs

@kitten kitten merged commit 10dd75f into main Nov 16, 2020
@kitten kitten deleted the fix/lazy-suspense-await branch November 16, 2020 20:17
@github-actions github-actions bot mentioned this pull request Nov 16, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants