I'm utilizing renderToStringWithData for SSR, but I've encountered an issue where, when opening more than one tab and accessing the site simultaneously, one of the tabs renders without waiting for the request to complete. I need assistance in understandin

Hello,

I am currently working on a Next.js application using Apollo Client, specifically utilizing renderToStringWithData for server-side rendering (SSR). I have come across a peculiar issue: when the application is accessed from multiple browser tabs simultaneously, one of the tabs seems to render the page without waiting for the data fetching request to fully complete.

This behavior is inconsistent, as it only happens during simultaneous requests, suggesting a potential race condition or a performance issue under high concurrency/load. The expected behavior is that all pages, regardless of how they are accessed, wait for the data fetching to complete before being rendered, ensuring data consistency and a reliable user experience.

Here are some specifics of my setup:

  • Next.js version: [12.2.2]
  • Apollo Client version: [3.6.8]
  • Warn messages : [Warning: Detected multiple renderers concurrently rendering the same context provider. This is currently unsupported.]

My code

  • _document.tsx
class MyDocument extends Document<CustomDocumentProps> {
  constructor(props: CustomDocumentProps) {
    super(props);
    const { __NEXT_DATA__, apolloState } = props;
    __NEXT_DATA__.apolloState = apolloState;
  }

  static async getInitialProps(ctx: DocumentContext) {
    await renderToStringWithData(<ctx.AppTree pageProps={{}} />);
    const initialProps = await Document.getInitialProps(ctx);
    const client = initializeApollo(undefined);
    const apolloState = client.extract();
    await client.clearStore();
    return {
      ...initialProps,
      apolloState,
    };
}

-_app.tsx

function MyApp({ Component, pageProps }: AppPageProps) {
  const client = useApollo();
  return (
    <ApolloProvider client={client}>
      <Component {...pageProps} />
    </ApolloProvider>
  );
}
  • apollo-cache.ts
export const APOLLO_STATE_PROP_NAME = "__APOLLO_STATE__";

let apolloClient: ApolloClient<NormalizedCacheObject> | null;

const createApolloClient = () => {
  return new ApolloClient({
    ssrMode:true,
    cache,
    ...
  });
};

export const initializeApollo = (initialState?: unknown) => {
  const _apolloClient = apolloClient ?? createApolloClient();

  if (initialState) {
    const existingCache = _apolloClient.cache.extract();
    // @ts-ignore
    const data = deepMerge(initialState, existingCache, {
      arrayMerge: (destinationArray, sourceArray) => [
        ...sourceArray,
        ...destinationArray.filter((d) => sourceArray.every((s) => !isEqual(d, s))),
      ],
    });

    _apolloClient.cache.restore(data);
  }
  if (typeof window === "undefined") return _apolloClient;
  if (!apolloClient) {
    apolloClient = _apolloClient;
  }

  return _apolloClient;
};

export const useApollo = () => {
  // @ts-ignore
  const state = typeof window !== "undefined" ? window.__NEXT_DATA__.apolloState : undefined;
  const client = useMemo(() => {
    return initializeApollo(state);
  }, [state]);

  return client;
};

I am trying to understand the underlying cause of this issue and am looking for guidance on how to ensure that rendering only occurs after all requests have successfully completed, even when multiple tabs are opened and accessed simultaneously. Any insights, suggestions, or recommendations would be greatly appreciated.

Thank you in advance for your assistance.

It seems a bit shady to me that initializeApollo(undefined) would return the same Apollo Client instance that you created with renderToStringWithData - that usually should not be working, as on the server that call should every time return a new Apollo Client instance.

Is there a chance that you have window polyfilled on the server, so it’s not undefined?

1 Like

Thank you for your response and insights.

Regarding the initializeApollo(undefined) behavior, it is indeed expected to return a new Apollo Client instance with each call on the server. I will double-check the implementation to ensure that there is no unintentional caching or reusing of the Apollo Client instance across requests, which should not be the case.

As for the window object, we do not intentionally polyfill it on the server-side in our application. However, I will investigate any third-party dependencies or configurations that might be introducing this polyfill unknowingly. The distinction between the server and client environments should be clear, and any blurring of this line could lead to the issues we are experiencing.

I will conduct a thorough review of our codebase and configuration to identify any discrepancies or misconfigurations contributing to this problem. Further insights or debugging tips are welcome.