はじめに
Suspense を使って遊んでるときに新しい気付きがあったので、備忘録として残します。いまのところの理解としてはフィールドのアクセスは メソッド呼び出しは
です。
環境
- react-router v7.3
- react v19
出会いは突然に
Suspense を使ってローディングを表示して、表示したい内容のフェッチが終わって無事に表示できたと思いきや、Chrome Developer Tools の console に次のようなメッセージがありました。
Uncaught Error: Hydration failed because the server rendered HTML didn't match the client. As a result this tree will be regenerated on the client. This can happen if a SSR-ed Client Component used:
- A server/client branch `if (typeof window !== 'undefined')`.
- Variable input such as `Date.now()` or `Math.random()` which changes each time it's called.
- Date formatting in a user's locale which doesn't match the server.
- External changing data without sending a snapshot of it along with the HTML.
- Invalid HTML tag nesting.
It can also happen if the client has a browser extension installed which messes with the HTML before React loaded.
https://react.dev/link/hydration-mismatch
詳細はわかっていませんが、どうやらサーバーサイドでレンダリングした結果とクライアントでレンダリングした結果に違いがあるときに起きるエラーのようです ただ、Suspense を使ってるだけなのに不思議でした。
原因
Qiita API から記事を取得して Article
クラスのインスタンスを作成して一覧を表示していたのですが、コンポーネントから getter format_created_at を呼び出していました。
<div>
{ article.format_created_at }
</div>
Article.ts
export class Article {
public created_at: string
constructor(
created_at: string
) {
this.created_at = created_at
}
get format_created_at(): string {
const date = new Date(created_at);
const year = date.getFullYear();
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`
}
}
どういう原因で Hydration failed となるかわかっていませんが、メソッド呼び出しをすると起きるようです ただ、フィールドでアクセスするとエラーなく表示することができます。