ロゴWeb開発ブログ

Next.js + Material UIでカラーモードをRecoilを使ってを切り替えて、設定をLocal Storageに保存する

作成
  • 使用したバージョン
  • mui/material 5.13.6
  • recoil 0.7.7

当サイトの右上にあるアイコンをクリックするとカラーモードの変更ができる。
設定をLocal Storageに保存してあるので次回アクセスしたときも同じカラーモードで表示できるようになる。

Recoilを使ったカラーモードの切り替え

まず、Atomの設定。
あとでLocal Storageの値をセットするため初期値はnullにしておく。


export const colorModeAtom = atom<PaletteMode|null>({
key: 'colorModeAtom',
default: null
});

カラーモードを切り替えるためのボタンを作成する。
ここではMaterial UIのIconを使用している。


export const ColorModeSwitch = () => {
const [colorMode, setColorMode] = useRecoilState(colorModeAtom);
const toggleColorMode = () => {
setColorMode(colorMode === 'light' ? 'dark' : 'light');
};
return (
<IconButton onClick={toggleColorMode} >
{colorMode === 'dark' ? <Brightness7 /> : <Brightness4 />}
</IconButton>
);
};

ライトモードとダークモードで使用するカラーの設定をする。
デフォルトカラーのまま使用する場合は、modeだけの切り替えでOK。


export const darkPalette: PaletteOptions = {
mode: 'dark',
primary: {
main: purple[900],
},
background: {
default: grey[800],
heading: blueGrey[900]
}
};
export const lightPalette: PaletteOptions = {
mode: 'light',
primary: {
main: purple[800],
},
background: {
default: grey[300],
heading: blueGrey[50]
},
};

ThemeProviderに渡すthemeをatomの値で切り替える。


export const defaultColorMode = 'dark';
export function Provider({ children }: PropsWithChildren) {
const colorMode = useRecoilValue(colorModeAtom)||defaultColorMode;
const theme = useMemo(() => createTheme({
palette: colorMode === 'dark' ? darkPalette : lightPalette
}), [colorMode]);
return (
<Provider theme={theme}>
{children}
</Provider>
);
}

Local Storageに設定を保存する

RecoilでLocal Storageに設定を保存する方法としてはAtomEffectを使用する方法もあるが、Next.jsを使用する場合、初期カラーを反映させるのがうまくいかなかったためuseEffectを使うことにする。

カラー切り替えボタンのuseEffect内でLocal Storageの読み込みや保存を行う。


export const ColorModeSwitch = () => {
const [colorMode, setColorMode] = useRecoilState(colorModeAtom);
const toggleColorMode = () => {
setColorMode(colorMode === 'light' ? 'dark' : 'light');
};
useEffect(() => {
const strageKey = 'mui_color_mode';
// 初回時にlocalStorageにあるカラーモードあるいはdefaultColorModeをセットする
if (colorMode === null) {
const savedColorMode = (window.localStorage.getItem(strageKey) || defaultColorMode) as PaletteMode;
setColorMode(savedColorMode);
// colorModeが変更されるたびにlocalStorageに保存する
} else {
localStorage.setItem(strageKey, colorMode);
}
}, [colorMode]);
return (
<IconButton onClick={toggleColorMode} >
{colorMode === 'dark' ? <Brightness7 /> : <Brightness4 />}
</IconButton>
);
};

初回レンダリング時にデフォルトのカラーが一瞬表示される問題

Local Storageにライトモードの設定を保存したとしても、デフォルトをダークモードにしていると一瞬ダークモードで表示されたのちライトモードへと変わる。
これは公式サイトでもそのような挙動になっているため、今のところ修正できない模様。