-
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
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
feat(solid-query): SSR streaming support #4840
Conversation
Changed query checkers to use `isInitialLoading` Fix hydration key bug for streaming SSR Add `deferStream` option for streaming SSR
Store the entire query state in the resource, so that we can leverage native resource hydration to hydrate the query on the client as it’s streamed in.
hydrate(queryClient(), { | ||
queries: [ | ||
{ | ||
queryKey: defaultedOptions.queryKey, | ||
queryHash: defaultedOptions.queryHash, | ||
state: info.value, | ||
}, | ||
], | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here I'm assuming that hydrating mutations from server isn't really a use case - or is it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah no need to hydrate the mutations here! Looks good
|
||
onCleanup(() => unsubscribe()) | ||
onCleanup(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Observing some strange behavior on the server with onCleanup
. Pop a log in here, and load one of the pages that contains two queries... note how onCleanup
is only triggered once on the server, despite two queries. Not sure why.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR I just created will solve this issue
})() | ||
}) | ||
if (!unsubscribe) { | ||
unsubscribe = createClientSubscriber( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For queries that were created in SSR, onHydrate
will be called on the client, at which point we can hydrate the queryClient
for that particular query, and then lazy subscribe to the observer.
} | ||
resolve(unwrap(state.data)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH, wasn't sure when/where/why to unwrap
versus not unwrap
... I understand what unwrap does, but any rules of thumb re where we need to use it?
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. Latest deployment of this branch, based on commit 43d3fb1:
|
@@ -1579,7 +1579,6 @@ describe('createQuery', () => { | |||
|
|||
// Fetch query | |||
expect(states[0]).toMatchObject({ | |||
data: undefined, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it important that this property be defined on the original state object (e.g. when we states.push({ ...state })
)? When accessing through the proxy, e.g. state.data
, it will come back as undefined as expected.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This fine for now! I think libraries built on top of solid-query would need to know about this caveat. That's worth a consideration
Thanks for working on this! I really like it. Any idea why |
… just one possible approach
Sorry I dont know what just happened. I accidentally edited your comment. But take a look at the codesandbox example created above! Doesn't seem to work on my device :/ Screen.Recording.2023-01-19.at.10.52.12.PM.mov |
Ah, maybe that behavior is due to 096da93? I branched off of your ssr branch, and then merged in latest v5 before starting my work. Perhaps trying switching back and forth between browser tabs to see if the refetch on visibility change occurs? |
That seems to be it! If I change tabs I can see it refetch on coming back to the tab |
const createClientSubscriber = (refetch: (info?: unknown) => void) => { | ||
return observer.subscribe((result) => { | ||
notifyManager.batchCalls(() => { | ||
const unwrappedResult = { ...unwrap(result) } | ||
setState(unwrappedResult) | ||
refetch() | ||
})() | ||
}) | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const createClientSubscriber = (refetch: (info?: unknown) => void) => { | |
return observer.subscribe((result) => { | |
notifyManager.batchCalls(() => { | |
const unwrappedResult = { ...unwrap(result) } | |
setState(unwrappedResult) | |
refetch() | |
})() | |
}) | |
} | |
const createClientSubscriber = (refetch: (info?: unknown) => void, hydrated: boolean = false) => { | |
if (hydrated) { | |
defaultedOptions.refetchOnMount = false | |
observer.setOptions(defaultedOptions) | |
} | |
return observer.subscribe((result) => { | |
notifyManager.batchCalls(() => { | |
const unwrappedResult = { ...unwrap(result) } | |
setState(unwrappedResult) | |
refetch() | |
})() | |
}) | |
} |
Can we also add this to the client subscriber? This would allow us to not refetch a query that was rendered on the server, even if a staleTime wasn't set. Similar to how CSR apps behave
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For sure - I'll give it a try now. Would also be nice if we could figure out an actual "ssr" test that covers that hydrate code branch, and this stale behavior. Will see if I can figure that out.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 done in 9fc9e3d. Haven't figured out the ssr test... was looking for inspiration, but can't even find tests in the solidjs repo that cover ssr related functionality.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work! I can add the tests for SSR in a separate PR. Could you resolve the conflicts before I run the CI workflow?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 done
Even if staleTime is not set
# Conflicts: # packages/solid-query/src/createBaseQuery.ts # packages/solid-query/src/types.ts
Codecov Report
Additional details and impacted files@@ Coverage Diff @@
## v5 #4840 +/- ##
=====================================
Coverage ? 90.76%
=====================================
Files ? 85
Lines ? 3488
Branches ? 881
=====================================
Hits ? 3166
Misses ? 301
Partials ? 21 Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. ☔ View full report at Codecov. |
# Conflicts: # pnpm-lock.yaml
The latest updates on your projects. Learn more about Vercel for Git ↗︎ 1 Ignored Deployment
|
@marbemac I made a PR with some fixes with this approach. The PR fixes two issues:
This also was the reason why the unsubscribe function wasn't working correctly on the server. It is fixed now. |
@ardeora great! I'll take a look in a few hours. |
fix(solid-query): SSR fixes
Hello @marbemac! Could you rebase again from the v5 branch? I'll create another PR to fix the svelte-query tests then :D If they haven't been fixed already and get this PR merged |
Sure thing! Can get that done today. |
# Conflicts: # packages/solid-query/src/__tests__/createQuery.test.tsx # packages/solid-query/src/createBaseQuery.ts # pnpm-lock.yaml
@ardeora should be all set |
Deployment failed with the following error:
|
@marbemac Thanks again for all the hard work on this. The changes have been merged now. v5 is going to be 💯 |
Working off of the progress in #4803.
This refactors
createBaseQuery
to an alternative approach that consolidates to a singlecreateResource
call. Instead of coordinating two createResource calls - one that sends down the dehydrated query state, and one that sends down the query data - this PR uses a single createResource that tracks the entire query state, and leverages solid's nativeonHydrate
functionality to hydrate that state on the client. This means we also get support for SSR streaming (and optional defer for specific requests just by setting thedeferStream
option)!I also expanded the
solid-start-streaming
example to better showcase the various ssr patterns (and make it easier to experiment in real world).