React v18 + NextjsでのHydration failedの回避
Nextjsを使用して、SSR対応のサイトを作成していたところエラーが発生するようになった。 SSRとクライアントレンダリングの間にHTML構造に差異があることが原因となっていた。
エラー内容
Error: Hydration failed because the initial UI does not match what was rendered on the server.
See more info here: https://nextjs.org/docs/messages/react-hydration-error
next-dev.js?3515:20 Warning: An error occurred during hydration. The server HTML was replaced with client content in <div>.
See more info here: https://nextjs.org/docs/messages/react-hydration-error
原因
サーバーでのレンダリングとクライアントでのレンダリングでHTMLのタグに差異があることがエラー発生の原因になっているらしい。
該当箇所
NavBarで、body変数内のレンダリングを変更させているところでエラーが出ていた。
import { Box, Button, Flex, Link } from "@chakra-ui/react";
import React from "react";
import NextLink from "next/link";
import { useLogoutMutation, useMeQuery } from "../generated/graphql";
import { isServer } from "../utils/isServer";
interface NavBarProps {}
export const NavBar: React.FC<NavBarProps> = ({}) => {
const [{fetching: logoutFetching}, logout] = useLogoutMutation();
const [{ data, fetching }] = useMeQuery({
pause: isServer(),
});
let body = null;
// data is loading
if (fetching) {
// user not logged in
} else if (!data?.me) {
body = (
<>
<NextLink href="/login">
<Link mr={2}>login</Link>
</NextLink>
<NextLink href="/register">
<Link mr={2}>register</Link>
</NextLink>
</>
);
// user is logged in
} else {
body = (
<Flex>
<Box mr={2}>{data.me.username}</Box>
<Button
onClick={() => {
logout(undefined);
}}
isLoading={logoutFetching}
variant="link"
>
logout
</Button>
</Flex>
);
}
return (
<Flex bg="tan" p={4}>
<Box ml={"auto"}>{body}</Box>
</Flex>
);
};
対策 (本質的でない)
navBarのコンポーネントを使用する場所で、 dynamic import を使用してnavBarコンポーネントのサーバーでのレンダリングを回避する。
import dynamic from "next/dynamic";
import React from "react";
import { Wrapper, WrapperVariant } from "./Wrapper";
interface LayoutProps {
children: React.ReactNode;
variant?: WrapperVariant;
}
const AvoidSSRNavBar = dynamic(() => import("./NavBar").then(modules => modules.NavBar), {ssr: false});
export const Layout: React.FC<LayoutProps> = ({ children, variant }) => {
return (
<>
<AvoidSSRNavBar />
{/* <NavBar /> */}
<Wrapper variant={variant}>{children}</Wrapper>
</>
);
};