Closed codew0nderer closed 1 year ago
Hi @codew0nderer! We have a similar issue reported over in the SuperJSON repo: https://github.com/blitz-js/superjson/issues/196 Could you check if they're related?
Wait, forget about that. Have you tried excluding the dehydratedState
property from babel-plugin-superjson-next
? See https://github.com/blitz-js/babel-plugin-superjson-next#options
Hi, sorry for the late reply just had the opportunity to get back to this issue.
Excluding dehydratedState from serialization will not make use of SuperJson and will throw an error (image below) if the dehydratedState includes values that are not JSON-compatible (like Date values).
The issue here is that when the serialized state is passed to _app.tsx, pageProps.dehydratedState is still not deserialized at that point, which means that <Hydrate state={pageProps.dehydratedState>
will be receiving a non-deserialized dehydratedState, and that will result in useQuery returning non-deserialized data (e.g: Date values as strings).
It is happening because the deserialization is done on page level as pages are wrapped with withSuperJSONPage, this can be noticed only with a large staleTime, so that react-query doesn't refetch immediately on client.
Snippets to reproduce the issue: _app.js
import { useState } from 'react';
import { Hydrate, QueryClient, QueryClientProvider } from '@tanstack/react-query';
function MyApp({ Component, pageProps }) {
const [queryClient] = useState(
() =>
new QueryClient({
defaultOptions: {
queries: {
retry: false,
refetchOnReconnect: false,
refetchOnWindowFocus: false,
staleTime: 10 * 1000 * 60, // 10 min - Long staleTime to not refetch immediately on client
cacheTime: 10 * 1000 * 60, // 10 min
},
},
})
);
return (
<QueryClientProvider client={queryClient}>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
</QueryClientProvider>
);
}
export default MyApp;
index.js
import { dehydrate, QueryClient, useQuery } from '@tanstack/react-query';
const getPosts = () => ({ title: 'post', dateValue: new Date() });
export const getServerSideProps = async () => {
const queryClient = new QueryClient();
await queryClient.prefetchQuery(['posts'], getPosts);
return {
props: {
dehydratedState: dehydrate(queryClient),
testDate: new Date(),
},
};
};
export default function Home(props) {
const { data } = useQuery(['posts'], getPosts);
// The value here should be date and not string
console.log(data.dateValue); // '2022-12-07T09:41:20.924Z'
console.log(data.dateValue instanceof Date); // false
return <div>TEST</div>;
}
.babelrc
{
"presets": ["next/babel"],
"plugins": ["superjson-next"]
}
Hey @codew0nderer, thanks for the explanation! Thank you for providing these steps. I'm not super well-versed with react-query, could you provide me with a ready-made reproduction repository where I just have to run npm run dev
, some precise steps to reproduce, and an expected behaviour? That'd make it a lot easier for me to help you with this.
Hello @Skn0tt ,
Thank you for your reply.
You can find in the following repository a reproduction of the issue.
The expected behavior is for data
returned by useQuery
in index.js:18 to have a dateValue of Date type and not string. I also included the workaround with comments if you would like to check it.
Let me know if you need any more details.
Thank you for your help 😊
Thanks for providing the repro! Sorry for not replying earlier, was a bit under water ...
I tried setting it up, and running yarn install
gave me error An unexpected error occurred: "http://verdaccio.lab.mwc.bc/@tanstack%2fquery-core/-/query-core-4.20.4.tgz: getaddrinfo ENOTFOUND verdaccio.lab.mwc.bc".
. I think this is because of some special yarn config you have. I've removed the yarn.lock
file locally and it works now - just thought i'd let you know, in case this turns out the be relevant for the bug.
I see you have set up both babel-plugin-superjson-next
and next-superjson-plugin
in your project. These plugins do the same thing - one is a SWC plugin, one a babel plugin. Could you try removing one of them, and check if the error still exists?
Indeed the resolve url was incorrect I have fixed that.
As for the dependencies I have both listed because I wanted to make sure that the issue is not happening just with the babel pluging, so I installed the SWC one and tested it as well, but it gave the same result.
I have deleted the SWC pluging from the reproduction repository dependencies and double checked again, the result is the same.
Let me know if you need any more details or help to reproduce or understand the issue.
Thank you for your time and help 😊
Thanks for updating the repository! I was able to start the dev server. Seeing the code live made it a lot easier for me to understand what this issue is about :)
It looks like you have the same issue that was discussed in https://github.com/blitz-js/babel-plugin-superjson-next/issues/93, and you also found the same workaround :D The author of that original issue shipped a PR that adds a small tool which makes that easier to use: https://github.com/blitz-js/babel-plugin-superjson-next/pull/94
I've opened a PR to your reproduction repo to demonstrate how you might use it: https://github.com/codew0nderer/nextjs-superjson-playground/pull/1
I'm assuming this solves your issue, so I'll close. Feel free to re-open if this doesn't :)
Hello,
I would like to point out an issue when working with React Query and NextJs, that I don't think is the expected behavior, but correct me if I am wrong.
It occurs when prefetching queries using hydration. As mentioned in React Query docs, to prefetch using hydration the component in _app.tsx is wrapped in
<Hydrate>
which takes the pageProps.dehydratedState as prop.Snippet to reproduce the behavior:
But from my understanding since WithSuperJSON is wrapped around the page and not the _app.tsx component, that means that the pageProps.dehydratedState in_app.tsx is not deserialized, and as a result the
<Hydrate>
end up having a non deserialized dehydratedState which is saved in cache by React Query and later used by all useQuery hooks.As a workaround to this, the pageProps could be explicitly deserialized in _app.tsx and passed to
<Hydrate>
like the following:I can provide a git repository reproducing the behavior if the shared snippets are not enough.
Thank you!