Search code examples
next.jsinternationalizationi18nextreact-i18nexturql

Error when I use getServerSideProps: Hydration failed because the initial UI does not match what was rendered on the server


I am using an Urql client to make a query to my graphql server. My query looks like this:

const username = () => {
  const { t: translation } = useTranslation(["common", "profile"]);

  const { query, locale } = useRouter();
  const username = query.username;
  const [{ data: userProfileData }] = useUserProfileQuery({
    variables: {
      username: username as string,
    },
    pause: isServer || !username,
  });
  const userProfile = userProfileData?.userProfile;

  return (
    <Text>{userProfile.name}</Text>
  );
};

export default withUrqlClient(createUrqlClient, { ssr: false })(username);

This would be fine, but because I am using the next-i18next library to localise my website, I have to add the getServerSideProps function.

export const getServerSideProps = async ({ locale, locales}: any) => {
  return {
    props: {
      ...(await serverSideTranslations(
        locale,
        ["common", "profile", "settings"],
        null,
        locales
      )),
    },
  };
};

This all works fine, but it means I can't server-side render my page by setting ssr to true like the following:

export default withUrqlClient(createUrqlClient, { ssr: false })(username);

My goal is to make the query on the useUserProfileQuery server.

I read the following guide: https://formidable.com/open-source/urql/docs/advanced/server-side-rendering/#ssr-with-getstaticprops-or-getserversideprops. Which suggested that I do the query in getServerSideProps. As a result, the new getServerSideProps function looks like this:

export const getServerSideProps = async ({ locale, locales, query }: any) => {
  const ssrCache = ssrExchange({ isClient: false });
  const client = initUrqlClient(createUrqlClient(ssrCache), false);
  if (client)
    await client
      .query(UserProfileDocument, {
        username: query.username,
      })
      .toPromise();

  return {
    props: {
      ...(await serverSideTranslations(
        locale,
        ["common", "profile", "settings"],
        null,
        locales
      )),
      urqlState: ssrCache.extractData(),
    },
  };
};

But, I get the following error:

Error: Hydration failed because the initial UI does not match what was rendered on the server.

Warning: Expected server HTML to contain a matching <div> in <div>.

See more info here: https://nextjs.org/docs/messages/react-hydration-error

What am I doing wrong? Please help.


Solution

  • The solution is to return userProfile in getServerProps and remove the query from the username function.

    Here's what the new getServerProps looks like

    export const getServerSideProps: GetServerSideProps = async ({
      locale,
      locales,
      query,
    }: any) => {
      const ssrCache = ssrExchange({ isClient: false });
      const client = initUrqlClient(createUrqlClient(ssrCache), false);
      let userProfile = null;
      if (client)
        userProfile = await client
          .query(UserProfileDocument, {
            username: query.username,
          })
          .toPromise();
    
      return {
        props: {
          ...(await serverSideTranslations(
            locale,
            ["common", "profile", "settings"],
            null,
            locales
          )),
          userProfile: JSON.parse(JSON.stringify(userProfile?.data?.userProfile)),
        },
      };
    };
    

    This is what the new username function looks like

    const username = ({locale, userProfile}: any) => {
      const { t: translation } = useTranslation(["common", "profile"]);
    
      return (
        <Text>{userProfile.name}</Text>
      );
    };
    export default withUrqlClient(createUrqlClient, { ssr: false })(username);