Skip to content
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

Broken rendering when using ssrForceFetchDelay #128

Open
onderonur opened this issue Apr 13, 2020 · 0 comments
Open

Broken rendering when using ssrForceFetchDelay #128

onderonur opened this issue Apr 13, 2020 · 0 comments

Comments

@onderonur
Copy link

Greetings!

I have a simple setup to server-side render my pages. I simply wrap _app.tsx with withApollo and pass getDataFromTree from @apollo/react-ssr.

Everything works great, some of my queries are network-only. So the apollo documentation for server-side rendering says that:

If you are using fetchPolicy: network-only or fetchPolicy: cache-and-network on some of the initial queries, you can pass the ssrForceFetchDelay option to skip force fetching during initialization, so that even those queries run using the cache.

Basically, it prevents these queries to be fetched the second time on the client-side.

I'm using this config but when I navigate between pages, or especially when I refresh the pages with F5, some parts of the page gets rendered broken and console logs errors like this:
Warning: Expected server HTML to contain a matching <a> in <div>.

When I increase the value (like to 1000) that I've passed to ssrForceFetchDelay, this error goes away. But when I use smaller values (like 100), this error occurs. If the value is smaller, the error becomes more consistently occuring.

I'm not sure if this is the right place to open this issue. If not, my apologies... But a lot of people use this package to integrate Apollo with Next.js. And if my problem is valid, some documentation might help us.

My withApollo.tsx is configured like this:

import React from "react";
import withApollo from "next-with-apollo";
import { ApolloProvider } from "@apollo/react-hooks";
import {
  InMemoryCache,
  IntrospectionFragmentMatcher,
} from "apollo-cache-inmemory";
import { createUploadLink } from "apollo-upload-client";
import { Operation, ApolloLink, Observable } from "apollo-link";
import { onError } from "apollo-link-error";
import introspectionQueryResultData from "@/generated/fragmentTypes.json";
import ApolloClient from "apollo-client";
import { isServer } from "@/utils";
import nookies from "nookies";

export default withApollo(
  ({ initialState = {}, ctx }) => {
    const request = (operation: Operation) => {
      operation.setContext({
        headers: {
          "x-xsrf-token": nookies.get(ctx).csrfToken,
          // Passing cookies while SSR
          cookie: isServer() ? ctx?.req?.headers.cookie : undefined,
        },
      });
    };

    const requestLink = new ApolloLink(
      (operation, forward) =>
        new Observable((observer) => {
          // eslint-disable-next-line @typescript-eslint/no-explicit-any
          let handle: any;
          Promise.resolve(operation)
            .then((operation) => request(operation))
            .then(() => {
              handle = forward(operation).subscribe({
                next: observer.next.bind(observer),
                error: observer.error.bind(observer),
                complete: observer.complete.bind(observer),
              });
            })
            .catch(observer.error.bind(observer));

          return () => {
            if (handle) {
              handle.unsubscribe();
            }
          };
        }),
    );

    // https://www.apollographql.com/docs/react/data/fragments/#fragments-on-unions-and-interfaces
    const fragmentMatcher = new IntrospectionFragmentMatcher({
      introspectionQueryResultData,
    });

    const cache = new InMemoryCache({ fragmentMatcher }).restore(initialState);

    // https://www.apollographql.com/docs/react/migrating/boost-migration/#after-1
    const client = new ApolloClient({
      link: ApolloLink.from([
        onError(({ graphQLErrors, networkError }) => {
          if (graphQLErrors)
            graphQLErrors.forEach(({ message, locations, path }) =>
              // eslint-disable-next-line no-console
              console.log(
                `[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
              ),
            );
          if (networkError) {
            // eslint-disable-next-line no-console
            console.log(`[Network error]: ${networkError}`);
          }
        }),
        requestLink,
        createUploadLink({
          uri: "http://localhost:3000/api/graphql",
        }),
      ]),
      cache,
      ssrMode: isServer(),
      // Note for Next.js:
      // If you set this to a small value like "100", it will cause a rendering value.
      // e.g, Expected server HTML to contain a matching <a> in <div>.
      // And the page will look broken.
      // https://www.apollographql.com/docs/react/performance/server-side-rendering/
      // If you are using fetchPolicy: network-only or fetchPolicy: cache-and-network on some of the initial queries,
      // you can pass the ssrForceFetchDelay option to skip force fetching during initialization,
      // so that even those queries run using the cache:
      ssrForceFetchDelay: 1000,
    });
    return client;
  },
  {
    render: ({ Page, props }) => {
      const { apollo } = props;
      return (
        <ApolloProvider client={apollo}>
          <Page {...props} />
        </ApolloProvider>
      );
    },
  },
);

Note: I'm using apollo-upload-client if it matters.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant