Skip to content

Commit

Permalink
fix: client hydration errors
Browse files Browse the repository at this point in the history
Fixing the following error:

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

This was due to the startTime being different on the server than on the
client since on the server it is in Eastern time but on the client it
uses the users local time zone (which may be different).

This is fixed by using a `useHydration` hook with a Suspense boundary.

More info here:

https://francoisbest.com/posts/2023/displaying-local-times-in-nextjs
  • Loading branch information
smoak committed Dec 17, 2023
1 parent 698e728 commit 40c3932
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { StartTime } from "~/components/StartTime";
import { TeamInfo } from "~/components/TeamInfo";
import type { ScheduledGame } from "~/components/types";

Expand All @@ -19,9 +20,7 @@ export const ScheduledGameCardContents = ({
/>
<div className="mt-3 flex flex-1">
<p className="flex-1 whitespace-nowrap px-3 pt-1.5 text-center uppercase">
{new Intl.DateTimeFormat("en-US", { timeStyle: "short" }).format(
new Date(game.startTime)
)}
<StartTime date={new Date(game.startTime)} />
</p>
</div>
<TeamInfo
Expand Down
30 changes: 30 additions & 0 deletions app/components/StartTime/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Suspense, useEffect, useState } from "react";
import { useHydration } from "~/hooks/useHydration";

export type StartTimeProps = {
readonly date: Date;
};

const useLocales = (): string[] => {
const [locales, setLocales] = useState(["en"]);

useEffect(() => {
setLocales([...window.navigator.languages]);
}, []);

return locales;
};

export const StartTime = ({ date }: StartTimeProps) => {
const iso = date.toISOString();
const hydrated = useHydration();
const locales = useLocales();

return (
<Suspense key={hydrated ? "local" : "utc"}>
<time dateTime={iso} title={iso}>
{new Intl.DateTimeFormat(locales, { timeStyle: "short" }).format(date)}
</time>
</Suspense>
);
};
11 changes: 11 additions & 0 deletions app/hooks/useHydration.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { useEffect, useState } from "react";

export const useHydration = (): boolean => {
const [hydrated, setHydrated] = useState(false);

useEffect(() => {
setHydrated(true);
}, []);

return hydrated;
};

1 comment on commit 40c3932

@vercel
Copy link

@vercel vercel bot commented on 40c3932 Dec 17, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Successfully deployed to the following URLs:

nhl-remix – ./

nhl-remix-smoak.vercel.app
nhl-remix-git-main-smoak.vercel.app
nhl-remix.vercel.app

Please sign in to comment.