Skip to content

Hydration API can not handle undefined values #1978

Closed

Description

@iksent

Describe the bug
This issue is very similar to this one: #1661

To Reproduce

  1. Next.js + ReactQuery + GraphQL + TypeScript + GraphQL codegen
  2. My Code:

[pages/_app.tsx]

class MyApp extends App<AppProps> {
  queryClient: QueryClient | null = null

  constructor(props) {
    super(props)

    this.queryClient = new QueryClient({
      defaultOptions: {
        queries: {
          refetchOnWindowFocus: false,
          refetchOnMount: false,
          cacheTime: 1000 * 60 * 100,
          retryDelay: 1000 * 5,
          staleTime: 1000 * 60 * 10,
        },
      },
    })
  }

  static async getInitialProps(appContext: AppContext) {
    return await appGetInitialProps({
      appContext,
      redirectRoute: ROUTES.main.index,
      isPrivatePath: () => false,
    })
  }

  render() {
    const { pageProps, Component } = this.props
    console.log('pageProps', pageProps.dehydratedState)
    return (
      <WithProviders store={store} dialogVariants={dialogVariants}>
        <QueryClientProvider client={this.queryClient}>
          <Hydrate state={pageProps.dehydratedState}>
            <MainLayout>
              <Component {...pageProps} />
            </MainLayout>
          </Hydrate>
          <ReactQueryDevtools initialIsOpen={false} />
        </QueryClientProvider>
      </WithProviders>
    )
  }
}

[pages/index.tsx]

export const _getHomepageArticles = fetcher<
  GetHomepageArticlesQuery,
  GetHomepageArticlesQueryVariables
>(GetHomepageArticlesDocument)

export const useGetHomepageArticlesQuery = <
  TData = GetHomepageArticlesQuery,
  TError = unknown
>(
  variables?: GetHomepageArticlesQueryVariables,
  options?: UseQueryOptions<GetHomepageArticlesQuery, TError, TData>,
) =>
  useQuery<GetHomepageArticlesQuery, TError, TData>(
    ['getHomepageArticles', variables],
    _getHomepageArticles,
    options,
  )

const Index = () => {
  const { data, ...other } = useGetHomepageArticlesQuery()
  console.log('Index Page:', data, 'params:', other)
  return <IndexPage />
}

export const getServerSideProps: GetServerSideProps = async (context) => {
  const queryClient = new QueryClient()
  await queryClient.prefetchQuery('getHomepageArticles', _getHomepageArticles)
  console.log('Result Cache:', queryClient.getQueryCache())
  return {
    props: {
      dehydratedState: dehydrate(queryClient),
    },
  }
}

Expected behavior
useGetHomepageArticlesQuery should return filled in data param.

Current behavior
useGetHomepageArticlesQuery returns data = undefined and isFetching = true (data is being loaded at the client = preloaded state is being ignored).

Logs Screenshot

Снимок экрана 2021-03-16 в 14 21 37

Desktop:

  • OS: MacOS M1
  • Browser: Chrome
  • RQ Version: Tried latest and 3.5.13 from the issue above.

Activity

  1. iksent commented on Mar 17, 2021

    @iksent
    Author

    Tried to pass initialData to hook from dehydrated state, like: pageProps.dehydratedState.queries[0].state.data and then it works like expected: data from hook is not empty, isLoading = false.

    BUT I expected it to work automatically, so that useQuery extracts the preloaded data from <Hydrate>.
    Isn't that how it should work?

  2. iksent commented on Mar 17, 2021

    @iksent
    Author

    I have found a problem:
    2021-03-17_14-17-17
    This is due to the different query keys that are generated from the string key and variables.
    So you need to be very accurate by using Codegen for this library.


    But I can't understand this situation:

    If you extract the key like this (automatically with codegen):

    useGetCategoriesQuery.getKey = (variables?: GetCategoriesQueryVariables) => [
      'getCategories',
      variables,
    ]
    

    And pass no variables:

        queryClient.prefetchQuery(
          useGetCategoriesQuery.getKey(), <<<< HERE
          fetcher<GetCategoriesQuery, GetCategoriesQueryVariables>(GetCategoriesDocument),
        ),
    

    then you'll get this error:

    SerializableError: Error serializing `.dehydratedState.queries[1].queryKey[1]` returned from `getServerSideProps` in "/".
    Reason: `undefined` cannot be serialized as JSON. Please use `null` or omit this value.
    

    But you can safely use the query hook without any error:

    export const useGetCategoriesQuery = <TData = GetCategoriesQuery, TError = unknown>(
      variables?: GetCategoriesQueryVariables,
      options?: UseQueryOptions<GetCategoriesQuery, TError, TData>,
    ) =>
      useQuery<GetCategoriesQuery, TError, TData>(
        ['getCategories', variables], <<<< HERE IS UNDEFINED TOO!
        fetcher<GetCategoriesQuery, GetCategoriesQueryVariables>(
          GetCategoriesDocument,
          variables,
        ),
        options,
      )
    
      const {
        isLoading: catsLoading,
        data: catsData,
        error: catsError,
      } = useGetCategoriesQuery()
    
  3. GZLiew commented on Apr 1, 2021

    @GZLiew

    same here for me its working in development but when I build the app. It stops working

  4. Ephem commented on Apr 14, 2021

    @Ephem
    Collaborator

    Seems you've encountered an undocumented but known limitation of the hydration API, sorry about that, until we have a fix we should at least document this as a known quirk.

    What is happening is that anything returned from getInitialProps is automatically converted to JSON, included in the html and then that JSON is parsed back and put into the cache. undefined can't be serialised to JSON which is why you are getting this error. When just using useQuery there is no JSON-stuff happening behind the scenes, which is why undefined values work there.

    The workaround for now is to ensure you never have undefined values in your data or keys, but we are thinking about ways to improve this.

  5. changed the title [-]ReactQuery + Next.js: preloaded data is being ignored[/-] [+]Hydration API can not handle undefined values[/+] on Apr 14, 2021
  6. TkDodo commented on Jun 3, 2021

    @TkDodo
    Collaborator

    @iksent would you like to open a PR to the docs ?

  7. sparkplug0025 commented on Jun 7, 2021

    @sparkplug0025

    I think another side-effect to this is the Nextjs parsing error on development. We are getting this exact issue here: https://github.com/eoscommunity/Eden/blob/15dd7146e81ef6707f156a8808c4d94d86d1d006/packages/webapp/src/pages/members/%5Bid%5D.tsx#L20

    The query returns undefined, because it can't find a member (and in Typescript we use undefined for a lot of niceties). Then when we dehydrate this and try to load the prefetched query we get the ugly error pointed in the above discussion.

  8. TkDodo commented on Sep 18, 2021

    @TkDodo
    Collaborator

    Let’s please document that hydration api needs serializable values. I think Error instances have a similar problem, and there is this comment to show how to work with custom serializers to make it work: #1208 (comment)

  9. tannerlinsley commented on Oct 9, 2021

    @tannerlinsley
    Member

    🎉 This issue has been resolved in version 3.26.0 🎉

    The release is available on:

    Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions