- 
          
- 
                Notifications
    You must be signed in to change notification settings 
- Fork 3.5k
fix(solid-query): hydrate preloaded data correctly #5775
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
Conversation
Signed-off-by: marbemac <marbemac@gmail.com> # Conflicts: # packages/solid-query/src/createBaseQuery.ts
Signed-off-by: marbemac <marbemac@gmail.com> # Conflicts: # packages/solid-query/src/createBaseQuery.ts
Signed-off-by: marbemac <marbemac@gmail.com>
Signed-off-by: marbemac <marbemac@gmail.com> # Conflicts: # examples/solid/solid-start-streaming/package.json # pnpm-lock.yaml
Signed-off-by: marbemac <marbemac@gmail.com>
Signed-off-by: marbemac <marbemac@gmail.com>
| The latest updates on your projects. Learn more about Vercel for Git ↗︎ 1 Ignored Deployment
 | 
| "test:types": "tsc", | ||
| "test:lib": "vitest run --coverage", | ||
| "test:lib:dev": "pnpm run test:lib --watch", | ||
| "test:lib:dev": "vitest watch --coverage", | 
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.
vitest --watch is a thing, vitest run --watch isn't - they have a dedicated watch command instead.
| if (process.env['NODE_ENV'] === 'development') { | ||
| console.error(unwrappedResult.error) | ||
| } | 
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.
Wasn't sure if this log is supposed to be here or an accidental leftover - lmk if it should stay and I can add it back.
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.
We added a log in there because of this discussion https://discord.com/channels/719702312431386674/1072140965910949929
Please let me know if you have a better way to resolve/fix this 😅
| if (unwrappedResult.isSuccess) { | ||
| // Use of any here is fine | ||
| // We cannot include refetch since it is not serializable | ||
| resolve(unwrappedResult as any) | ||
| } else { | ||
| resolve(unwrappedResult) | 
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.
IMHO we should always fall back to calling resolve(). I ran into an issue where neither reject nor resolve were called, resulting in a hung promise (and this was on the server, resulting in the entire page being hung). LMK if there's a reason to sometimes not resolve the promise.
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.
If we dont call reject here. How do we throw an error to the ErrorBoundary on the server?
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.
Well it's still calling reject like it was before. There was just an additional check where if we decided not to reject, we would only call resolve under certain conditions. Thus there was a scenario where neither reject NOR resolve would get called, leaving a hanging promise. Does that make sense?
| resolve(state) | ||
| const query = observer.getCurrentQuery() | ||
| resolve(hydrateableObserverResult(query, state)) | 
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 is the code path that wasn't covered - this is triggered if the data is already pre-loaded prior to createQuery. We need to "sanitize" the state on the server before resolving, so that it can be correctly hydrated back to the client.
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.
Awesome! This is looking great! Thanks for working on this :D
| 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 b57acbf: 
 | 
| Codecov ReportPatch coverage has no change and project coverage change:  
 ❗ Your organization is not using the GitHub App Integration. As a result you may experience degraded service beginning May 15th. Please install the Github App Integration for your organization. Read more. Additional details and impacted files@@             Coverage Diff             @@
##             beta    #5775       +/-   ##
===========================================
- Coverage   97.11%   83.97%   -13.14%     
===========================================
  Files          50       11       -39     
  Lines        2391      181     -2210     
  Branches      706       36      -670     
===========================================
- Hits         2322      152     -2170     
+ Misses         67       25       -42     
- Partials        2        4        +2     ☔ View full report in Codecov by Sentry. | 
| ☁️ Nx Cloud ReportCI is running/has finished running commands for commit b57acbf. As they complete they will appear below. Click to see the status, the terminal output, and the build insights. 📂 See all runs for this branch ✅ Successfully ran 1 targetSent with 💌 from NxCloud. | 
| resolve(state) | ||
| const query = observer.getCurrentQuery() | ||
| resolve(hydrateableObserverResult(query, state)) | 
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.
Awesome! This is looking great! Thanks for working on this :D
| if (unwrappedResult.isSuccess) { | ||
| // Use of any here is fine | ||
| // We cannot include refetch since it is not serializable | ||
| resolve(unwrappedResult as any) | ||
| } else { | ||
| resolve(unwrappedResult) | 
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.
If we dont call reject here. How do we throw an error to the ErrorBoundary on the server?
| if (process.env['NODE_ENV'] === 'development') { | ||
| console.error(unwrappedResult.error) | ||
| } | 
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.
We added a log in there because of this discussion https://discord.com/channels/719702312431386674/1072140965910949929
Please let me know if you have a better way to resolve/fix this 😅
Copied from #5775, rebased on
betabranch.Make sure that on the server we always resolve the resource data to a hydrate-able value, otherwise solid will silently call
onHydratedon the client with an empty object which can cause a variety of problems.The specific code branch we were missing was when
createQueryis called andobserver.getOptimisticResult(defaultedOptions)returns an already loaded query (if, for example,preloadQuerywas called prior to the components mounting - for example with router data loaders).As always, these SSR / hydration cases are difficult to test, so I added an example to the
solid-start-streamingto manually verify for now. This example simply callspreloadQueryon the server, and then renders a component that uses that same query.Before 1
This is with
deferStream: falseon the user query - note how the content ends up getting rendered twice, and in the dev console you can see thatonHydratedis called with an empty object. Things work as expected after the changes in this PR.Before 2
This is with
deferStream: trueon the user query - note how it ends up calling the user query on the server AND on the client (see logs in dev console), and in the dev console you can see thatonHydratedis called with an empty object (which causes the client to think that it needs to refetch, hence the client call).After