10/14/2021
Bruno
Então, eu tive um pouco de dificuldade atualizando esse blog para usar o Material UI v5. Eu segui a guia de migração que os autores sugerem mas tudo ainda ficou quebrado, com erro de mismatch de nomes de classes de CSS entre o cliente e o servidor. I sabia que o problema era relacionado a CSS mas demorou um pouco pra eu descobrir que o culpado real foi o hook useDarkMode que eu usava pra trocar entre o tema dark/escuro e o tema light com SSR, persistência, suporte pra fallback das preferências do usuário, e sem "flash" de conteúdo.
Acabei decidindo remover esse hook. Parece que não funciona bem trocando temas agora que o Material UI descontinuou o JSS em favor do Emotion.
O que eu fiz foi implementar um ThemeContext pra tratar disso, usando cookies, inspirado pela solução implementada do site dos docs do MUI, e parece que funcionou bem o suficiente, sem flash de conteúdo (no meu código real, tudo tá organizado em arquivos separados, mas pra facilitar esse post):
import {
createTheme,
PaletteMode,
useMediaQuery,
responsiveFontSizes,
ThemeOptions,
} from '@mui/material';
import {
createContext,
useCallback,
useContext,
useEffect,
useMemo,
useState,
} from 'react';
import { ThemeProvider as MuiThemeProvider } from '@mui/material';
function getThemeOptions(mode: PaletteMode): ThemeOptions {
return {
// theme options
palette: {
mode,
...(mode === 'light'
? {
// light palette
}
: {
// dark palette
}),
},
};
}
function buildTheme(mode: PaletteMode) {
return responsiveFontSizes(createTheme(getThemeOptions(mode)));
}
function getCookie(name: string) {
const regex = new RegExp(`(?:(?:^|.*;*)${name}*=*([^;]*).*$)|^.*$`);
return document.cookie.replace(regex, '$1');
}
type ThemeContextType = {
mode: PaletteMode;
setMode: (mode: PaletteMode | null) => void;
toggleMode: () => void;
};
export const ThemeContext = createContext<ThemeContextType>({
mode: 'light',
setMode: () => {},
toggleMode: () => {},
});
export const ThemeProvider: React.FC = ({ children }) => {
const [colorMode, setColorMode] = useState<PaletteMode | null>(null);
const prefersDarkMode = useMediaQuery('@media (prefers-color-scheme: dark)');
const preferredColorMode: PaletteMode = prefersDarkMode ? 'dark' : 'light';
useEffect(() => {
if (process.browser) {
const nextColorMode = getCookie('colorMode') as PaletteMode | string;
if (nextColorMode !== 'light' && nextColorMode !== 'dark') {
setColorMode(null);
} else {
setColorMode(nextColorMode);
}
}
}, []);
useEffect(() => {
document.cookie = `colorMode=${
colorMode ?? preferredColorMode
};path=/;max-age=31536000`;
}, [colorMode, preferredColorMode]);
const toggleMode = useCallback(() => {
setColorMode(mode => (mode === 'light' ? 'dark' : 'light'));
}, []);
const theme = useMemo(
() => buildTheme(colorMode ?? preferredColorMode),
[colorMode, preferredColorMode],
);
return (
<ThemeContext.Provider
value={{
mode: colorMode ?? preferredColorMode,
setMode: setColorMode,
toggleMode,
}}
>
<MuiThemeProvider theme={theme}>{children}</MuiThemeProvider>
</ThemeContext.Provider>
);
};
export const useThemeContext = () => useContext(ThemeContext);
Com isso, você pode trocar o ThemeProvider
no _app.tsx
, e usar o hook useThemeContext
pra alterar entre os modos de cor.
Até o próximo!