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
[legacy-framework] Fix dehydrateState does not apply server side props #2617
Conversation
@@ -76,7 +76,6 @@ describe("Queries", () => { | |||
describe("DehydratedState", () => { | |||
it("should work", async () => { | |||
const browser = await webdriver(context.appPort, "/dehydrated-state") | |||
await browser.waitForElementByCss("#content") |
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.
I definitely see why this line is problematic for the test.
@@ -11,7 +11,7 @@ import {Suspense} from "react" | |||
|
|||
export const getServerSideProps: GetServerSideProps = async (ctx) => { | |||
const queryClient = new QueryClient() | |||
const queryKey = getQueryKey(getDate, null) | |||
const queryKey = getQueryKey(getDate, 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.
Good catch spotting the wrong key argument! Maybe we can ship an abstraction with Blitz that makes these kinds of errors harder:
export const getServerSideProps = async ctx => {
return {
props: {
...(await prefetchedQueries([ getDate, undefined ], ...))
}
}
}
async function prefetchedQueries(...pairs) {
const queryClient = new QueryClient()
for (const [query, arg] of pairs) {
const key = getQuerykey(query, arg)
await queryClient.prefetchQuery(key, () => invokeWithMiddleware(query, arg, ctx))
}
return { dehydratedState: dehydrate(queryClient) }
}
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.
Interesting suggestion, I like it. It took me a while to figure it out and there was a small piece in the react-query docs that made me realize there was a mismatch.
Anyway we can make it easier for users to form the right code I am in support of :)
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 I knew from the start we needed to make this better.
How about this?
import {prefetchQuery} from 'blitz'
export const getServerSideProps = async ctx => {
await prefetchQuery(getData, undefined)
return { props: {} }
}
And then we automatically add new QueryClient()
, dehydrate
etc via a babel plugin.
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.
A small suggestion would be to require that dehydratedState
be passed into props: {}
to avoid the edge case of a user declaring a prop dehydratedState
and having overridden without them knowing by babel.
Perhaps we could rename the prop to prefetchedQuery
for clarity and then transform in blitz-root
?
export const getServerSideProps = async ctx => {
const dehydratedState = await prefetchQuery(getData, undefined)
return { props: {
dehydratedState
} }
}
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 babel plugin can throw a helpful error or something in that case, right? I think having dehydratedState
exposed is exposing unnecessary implementation details.
packages/core/src/blitz-app-root.tsx
Outdated
@@ -110,7 +109,7 @@ export function withBlitzAppRoot(UserAppRoot: React.ComponentType<any>) { | |||
}, []) | |||
|
|||
const dehydratedState = props.pageProps.dehydratedState | |||
? SuperJSON.deserialize(props.pageProps.dehydratedState) | |||
? props.pageProps.dehydratedState |
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.
It's interesting to me that removing the deserialisation helps in this case, since babel-plugin-superjson-next should be serializing dehydratedState
:
blitz/packages/babel-preset/src/index.ts
Line 16 in a28216d
require('babel-plugin-superjson-next'), |
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.
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, thanks for making that change!
I think I will add a test for some of the more complex types, like Map
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.
Interestly @Skn0tt, if I swap out getDate
with getComplexTypes
and return something like:
export default async function getComplexTypes() {
return {
date: new Date(0),
map: new Map().set("foo", "bar"),
obj: {
one: 1,
child: {
two: 2,
},
},
}
}
When map: new Map()
is in the returned object, the test fails:
NoSuchElementError: no such element: Unable to locate element: {"method":"css selector","selector":"#content"}
(Session info: headless chrome=92.0.4515.107)
77 | it("should work", async () => {
78 | const browser = await webdriver(context.appPort, "/dehydrated-state")
> 79 | const content = await browser.elementByCss("#content")
But when the map
key is omitted from the return object, the test passes.
Any ideas here, should this be supported?
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.
Yes, it should definitely be supported. That's some weird behaviour! Will look into 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.
Thank you @Skn0tt.
Let me know if I can lend a hand.
I'll park this PR until you've had a look 👍
Before, it just said `SuperJSON.deserialize(props.pageProps.dehydratedState)`. This didn't work since it doesn't account for babel-plugin-superjson-next to pick different superjson keys than SuperJSON itself :( SuperJSON expects a `json` and a `meta` key, while babel-plugin-superjson-next puts the meta right into the props and calls it `_superjson`. This commit fixes that behaviour, and makes sure that dehydratedState works both for SSR and for client-side.
@Skn0tt what's the status on this PR? |
looking at it now, let's hope I find something :) |
@flybayer @clgeoio the failure that @clgeoio found seems unrelated to this PR. Something seems to be mutating the SuperJSON before it's inlined into the |
Closes: blitz-js/legacy-framework#54
What are the changes and their implications?
The query that was setup in the dehydrated state did not have matching params (
null
vsundefined
). Additionally, the SuperJSON deserialisation seemed to be causing some issues with the cached query being used on the SSR.However, this change looks to undo: #2512
Bug Checklist
Feature Checklist