Closed3
ダークモードの対応ミスによる「Prop 'className' did not match.」
公式サンプル (https://github.com/mui-org/material-ui/tree/next/examples/nextjs) 通り対応するも「Prop 'className' did not match.」が直らなかったとき
該当コード
const getInitIsDark = () => {
// クライアントの場合 OS 設定を参照
if (typeof window !== "undefined") {
return matchMedia("(prefers-color-scheme: dark)").matches;
}
// サーバの場合ライトモード
return false;
};
const Theme = ({ children }) => {
const [isDark, setIsDark] = useState(getInitIsDark()); // 原因
// OS 設定が変更されたときの処理とか...
return (
<ThemeProvider theme={isDark ? DarkTheme : LightTheme}>
{children}
</ThemeProvider>
);
};
原因はダークモードかどうかの State を初期化する時に matchMedia
を使用しているため
ホワイトフラッシュ対策だったが、Next 環境で OS 設定がダークモードだと、サーバサイドとクライアントサイドのテーマの違いによって className に差が生じる
修正方法としては、副作用を伴う処理なのでちゃんと useEffect
で行うこと
useLayoutEffect
を使用すれば画面に描画される前に実行されるので、ホワイトフラッシュの回避も出来る (知らんかった)
// サーバサイドで `useLayoutEffect` を使用すると警告が出るので抑制
const useIsomorphicLayoutEffect =
typeof window !== "undefined" ? useLayoutEffect : useEffect;
const Theme = ({ children }) => {
const [isDark, setIsDark] = useState(false);
useIsomorphicLayoutEffect(() => {
const matchIsDark = matchMedia("(prefers-color-scheme: dark)").matches;
setIsDark(matchIsDark);
}, []);
return (
<ThemeProvider theme={isDark ? DarkTheme : LightTheme}>
{children}
</ThemeProvider>
);
};
Mui では上記を簡単にできる useMediaQuery
フックがあるので、今回はこれを使う
const Theme = ({ children }) => {
const isDark = useMediaQuery("(prefers-color-scheme: dark)");
return (
<ThemeProvider theme={isDark ? DarkTheme : LightTheme}>
{children}
</ThemeProvider>
);
};
このスクラップは2021/09/04にクローズされました