Material UI: Dark theme not being applied - reactjs

I'm using the Context API to store the theme value. The theme itself is created with <createMuiTheme> and passed down from <Layout> to children via <MuiThemeProvider> and <CssBaseline>. I can see the state change via React DevTools but the theme itself is not being applied - and I'm at loss as to why...
Here is codesandbox with a full example - warning: contains Samuel L. Ipsum. What follows is an abridged version.
Default and dark theme definitions:
// theme/dark.js
import { createMuiTheme } from "#material-ui/core/styles";
const theme = createMuiTheme({
typography: {
useNextVariants: true
},
palette: {
type: "dark"
}
});
export default theme;
// theme/default.js
import { createMuiTheme } from "#material-ui/core/styles";
const theme = createMuiTheme({
typography: {
useNextVariants: true
},
palette: {
type: "light"
}
});
export default theme;
Context:
// context/settings/SettingsContext.js
import React from "react";
export default React.createContext({
darkMode: false
});
// context/settings/SettingsProvider.js
import React, { useState } from "react";
import SettingsContext from "./SettingsContext";
const storage = {
getItem(key) {
if (localStorage) {
return localStorage.getItem(key);
}
},
setItem(key, value) {
if (localStorage) {
return localStorage.setItem(key, value);
}
}
};
const SettingsProvider = props => {
const [darkMode, setDarkMode] = useState(
storage.getItem("darkMode") === "true"
);
const onSetDarkMode = darkMode => {
setDarkMode(darkMode);
storage.setItem("darkMode", darkMode);
};
return (
<SettingsContext.Provider
value={{
darkMode,
onSetDarkMode
}}
>
{props.children}
</SettingsContext.Provider>
);
};
export default SettingsProvider;
index.js:
import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";
import App from "./containers/app";
import SettingsProvider from "./context/settings/SettingsProvider";
ReactDOM.render(
<BrowserRouter>
<SettingsProvider>
<App />
</SettingsProvider>
</BrowserRouter>,
document.getElementById("root")
app/index.js:
import React, { useState } from "react";
import { Switch, Route } from "react-router-dom";
import { default as home } from "../home/routes";
import Layout from "../layout";
const App = () => {
const [anchorEl, setAnchorEl] = useState(null);
return (
<div>
<Layout anchorEl={anchorEl} setAnchorEl={setAnchorEl}>
<Switch>
{home.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
render={route.render}
/>
))}
</Switch>
</Layout>
</div>
);
};
export default App;
And layout/index.js:
import React, { useContext } from "react";
import { MuiThemeProvider } from "#material-ui/core/styles";
import { makeStyles } from "#material-ui/core/styles";
import CssBaseline from "#material-ui/core/CssBaseline";
import defaultTheme from "../../themes/default";
import darkTheme from "../../themes/default";
import SettingsContext from "../../context/settings/SettingsContext";
import Header from "../../components/header/index";
const useStyles = makeStyles(theme => ({
toolbarMargin: {
...theme.mixins.toolbar
}
}));
const Layout = props => {
const classes = useStyles();
const context = useContext(SettingsContext);
const theme = context.darkMode ? darkTheme : defaultTheme;
const { children, anchorEl, setAnchorEl } = props;
return (
<MuiThemeProvider theme={theme}>
<CssBaseline />
<Header anchorEl={anchorEl} setAnchorEl={setAnchorEl} />
<main>
<div className={classes.toolbarMargin} />
{children}
</main>
</MuiThemeProvider>
);
};
export default Layout;
What did I miss?

You're importing the same theme twice. I'd suggest using named exports instead of defaults, it helps a lot with auto importing, as well as spotting mistakes like this.
// layout/index.js
import defaultTheme from "../../themes/default";
import darkTheme from "../../themes/default"; // should be "../../theme/dark"

Related

Chakra UI theme not rendering

I have followed the documentation of chakra UI and am trying to style my website. For some reason the config part for the dark/light mode works but the theme part does not work.
I have used create-react-app
This is my Index.js code-
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import { AuthProvider } from './Contexts/AuthProvider';
import { ChakraProvider, theme } from '#chakra-ui/react'
import defaultTheme from "./Themes/stylesEntryPoint"
import { ColorModeScript } from '#chakra-ui/react'
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<ChakraProvider theme={defaultTheme}>
<AuthProvider>
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
<App />
</AuthProvider>
</ChakraProvider>
);
This is the code for stylesEntryPoint file
import { extendTheme } from '#chakra-ui/react'
import {theme,config} from "./FoundationTheme/globalStyles"
const defaultTheme = {
theme , config
}
export default extendTheme(defaultTheme)
This is the code for globalStyles... I have copied it from chakra UI documentation here https://chakra-ui.com/docs/styled-system/global-styles
import { mode } from '#chakra-ui/theme-tools'
import { extendTheme } from '#chakra-ui/react'
export const config = {
initialColorMode: 'dark',
useSystemColorMode: true
}
export const theme = {
styles: {
global: (props) => ({
'html, body': {
fontSize: 'lg',
color: props.colorMode === 'dark' ? 'pink.100' : 'gray.600',
lineHeight: 'tall',
},
a: {
color: props.colorMode === 'dark' ? 'teal.300' : 'teal.500',
},
}),
},
}

Issue rendering dark mode - ReactJS

I'm new and trying a project to make a switch that toggles between two themes;
Here is what I have so far and I'm struggling to understand why it's not toggling between my themes.
theme.js
import { createContext } from "react";
export const themes = {
dark: "dark-mode",
light: "white-mode",
};
export const ThemeContext = createContext({
theme: themes.dark,
changeTheme: () => {},
});
themeWrapper.js
import React, { useState, useEffect } from 'react';
import { ThemeContext, themes } from './theme.js';
import "./App.css"
export default function ThemeContextWrapper(props) {
const [theme, setTheme] = useState(themes.dark);
function changeTheme(theme) {
setTheme(theme);
}
useEffect(() => {
switch (theme) {
case themes.light:
document.body.classList.add('white-mode');
break;
case themes.dark:
default:
document.body.classList.remove('white-mode');
break;
}
}, [theme]);
return (
<ThemeContext.Provider value={{ theme: theme, changeTheme: changeTheme }}>
{props.children}
</ThemeContext.Provider>
);
}
So I've got a wrapper to re-render the app when dark mode is toggled, and two themes that are being exported.
App.css
.white-mode {
font: #333;
background: #eee;
link: cornflowerblue;
}
.dark-mode {
font: #eee;
background: rgb(41, 41, 41);
link: lightblue;
}
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import reportWebVitals from './reportWebVitals';
import ThemeContextWrapper from '.themeWrapper';
ReactDOM.render(
<ThemeContextWrapper>
<React.StrictMode>
<App />
</React.StrictMode>{' '}
</ThemeContextWrapper>,
document.getElementById('root'),
);
So now in App.js I have the button
import './App.css';
import { Button, Container, InputGroup } from 'reactstrap';
import { ThemeContext, themes } from '.theme';
import React from 'react';
function App() {
const [darkMode, setDarkMode] = React.useState(true);
return (
<div className="App">
<header className="App-header">
<h1 className="text-warning">Dark/Light mode</h1>
<InputGroup>
<ThemeContext.Consumer>
{({ changeTheme }) => (
<Button
color="link"
onClick={() => {
setDarkMode(!darkMode);
changeTheme(darkMode ? themes.light : themes.dark);
}}
>
<span className="d-lg-none d-md-block">Toggle</span>
</Button>
)}
</ThemeContext.Consumer>
</InputGroup> */}
</header>
</div>
);
}
export default App;
But this button doesn't appear to do anything, where have I gone wrong?

Next.js / i18next: Language Selector for url with #hash

I am using the following Languageselector in my next project:
import React, { useState } from 'react'
import i18n from '../src/services/i18n'
import Link from 'next/link'
import { useRouter } from 'next/router'
const LanguageSelector = (props) => {
const [language, setLanguage] = useState(i18n.language);
const router = useRouter()
const handleOnclick = (value) => {
router.push(router.asPath, router.asPath, { locale: value });
};
console.log(router);
return (
<>
<div className={props.className!==undefined&&props.className!==""? ('d-inline-block d-xl-none '+props.className):"d-inline-block d-xl-none "} >
<select onChange={(e)=>{handleOnclick(e.target.value)}} required defaultValue={router.locale}>
<option value='de'>Deutsch</option>
<option value='en'>Enligsh</option>
</select>
</div>
<div className="d-none d-xl-block">
{router.locale==="en"? <div><Link href={router.asPath} locale='de'>DE</Link></div> : <div>DE</div>}
<p>|</p>
{router.locale==="de"? <div><Link href={router.asPath} locale='en'>EN</Link></div> : <div>EN</div>}
</div>
</>
)
}
export default LanguageSelector
_app.js
This does work fine until the visitor is on an URL, that has a # parameter. The URL in browser bar still changes but the language stays the same. How can I change the language on URLs with hash?
EDIT: This is how I am using translation
import Layout from '../components/Layout'
import { appWithTranslation } from 'next-i18next';
import React, {useState, useEffect} from 'react';
import { useRouter } from 'next/router'
const app = ({Component, pageProps })=> {
const router = useRouter()
function updateSchnellzugriffe(value, mobileValue){
setSchnellzugriffe(value);
setMobileSchnellzugriffe(mobileValue);
}
return (
<Layout>
<Component {...pageProps} />
</Layout>
)
}
export default appWithTranslation(app)
And this is an example of pageComponent
import React, {useEffect, useState} from 'react'
import { useTranslation } from 'next-i18next';
import { serverSideTranslations } from 'next-i18next/serverSideTranslations';
const home = (props) => {
const { t } = useTranslation(['common','home']);
return (
<h1 >{t("home:headline")}</h1>
)
};
export async function getStaticProps({ locale }){
return {
props: {
...await serverSideTranslations(locale, ['common','home']),
},
}
}
export default home

React Native Context - TypeError: undefined is not an object (evaluating '_useContext.theme')

I keep getting an error when I try to use Context API for dark/light mode in App.js
Theme.js
import React, { useState } from "react";
export const ThemeContext = React.createContext();
//theme = light, dark
export const ThemeProvider = ({ children }) => {
const [theme, setTheme] = useState("dark");
const toggleTheme = () => {
if (theme === "light") setTheme("dark");
else setTheme("light");
};
return (
<ThemeContext.Provider value={{ theme, toggleTheme }}>
{children}
</ThemeContext.Provider>
);
};
App.js
import { ThemeContext, ThemeProvider } from "./app/utility/ThemeManager";
export default function App() {
const { theme } = useContext(ThemeContext); // This is throwing the error
return (
<ThemeProvider>
...//Rest of my app
//How I'd like to use my theme
<StatusBar style={theme === "dark" ? "light" : "dark"} />
</ThemProvider>
);
};
I'd like to understand why this is throwing the error and how I could fix it?
Thanks in advance!
Import ThemeProvider in index.js
import { StrictMode } from "react";
import ReactDOM from "react-dom";
import { ThemeProvider } from "./Theme.js";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
<ThemeProvider>
<App />
</ThemeProvider>,
rootElement
);
and in App.js
import {ThemeContext} from "./Theme.js"
import StatusBar from './StatusBar';
import {useContext} from 'react'
export default function App() {
const { theme } = useContext(ThemeContext);
return (
<>
..... Rest Of your Code
</>
);
};

React Material UI Theme Change

can you please help me to change the React Material UI theme Dynamically .
https://imgur.com/S8zsRPQ
https://imgur.com/Ul8J40N
I have tried by changing the theme Properties on button click . The theme properties are getting changed as seen in the console . But the change is not reflecting on the theme .
Sandbox Code : https://codesandbox.io/s/30qwyk92kq
const themeChange = () => {
alert(theme);
theme.palette.type = "light";
console.log(theme);
};
ReactDOM.render(
<MuiThemeProvider theme={theme}>
<React.Fragment>
<CssBaseline />
<App changeTheme={themeChange} />
</React.Fragment>
</MuiThemeProvider>,
document.getElementById("app")
);
When I click the button the theme has to change to Dark color
I am using styledComponents, typescript and material-ui.
First I defined my themes:
// This is my dark theme: dark.ts
// I defined a light.ts too
import createMuiTheme from '#material-ui/core/styles/createMuiTheme';
export const darkTheme = createMuiTheme({
palette: {
type: 'dark', // Name of the theme
primary: {
main: '#152B38',
},
secondary: {
main: '#65C5C7',
},
contrastThreshold: 3,
tonalOffset: 0.2,
},
});
I defiend a themeProvider function and in this function I wrapped the material-ui's ThemeProvider in a React context to be able to change the theme easily:
import React, { useState } from 'react';
import {ThemeProvider} from "#material-ui/core/styles/";
import { lightTheme } from "./light";
import { darkTheme } from "./dark";
const getThemeByName = (theme: string) => {
return themeMap[theme];
}
const themeMap: { [key: string]: any } = {
lightTheme,
darkTheme
};
export const ThemeContext = React.createContext(getThemeByName('darkTheme'));
const ThemeProvider1: React.FC = (props) => {
// State to hold the selected theme name
const [themeName, _setThemeName] = useState('darkTheme');
// Retrieve the theme object by theme name
const theme = getThemeByName(themeName);
return (
<ThemeContext.Provider value={_setThemeName}>
<ThemeProvider theme={theme}>{props.children}</ThemeProvider>
</ThemeContext.Provider>
);
}
export default ThemeProvider1;
Now I can use it in my components like this:
import React from 'react';
import styled from 'styled-components';
import useTheme from "#material-ui/core/styles/useTheme";
const MyCardHeader = styled.div`
width: 100%;
height: 40px;
background-color: ${props => props.theme.bgColor};
color: ${props => props.theme.txtColor};
display: flex;
align-items:center;
justify-content: center;
`;
export default function CardHeader(props: { title: React.ReactNode; }) {
const theme = {
bgColor: useTheme().palette.primary.main,
txtColor: useTheme().palette.primary.contrastText
};
return (
<MyCardHeader theme={theme}>
{props.title}
</MyCardHeader>
);
}
For Changing between themes:
import React, {useContext} from 'react';
import { ThemeContext} from './themes/themeProvider';
export default function Header() {
// Get the setter function from context
const setThemeName = useContext(ThemeContext);
return (
<header>
<button onClick={() => setThemeName('lightTheme')}>
light
</button>
<button onClick={() => setThemeName('darkTheme')}>
dark
</button>
</header>
);
}
I'm using Material UI v4.
I tried something like Ashkan's answer, but it didn't work for me.
However, I found this in the documentation, and abstracting it to apply to a different piece of state, instead of user preference, worked for me. For your example, I'd probably make a context:
// context.js
import React, { useContext } from "react";
import { ThemeProvider, createTheme } from "#material-ui/core/styles";
import CssBaseline from "#material-ui/core/CssBaseline";
const CustomThemeContext = React.createContext();
// You can add more to these and move them to a separate file if you want.
const darkTheme = {
palette: {
type: "dark",
}
}
const lightTheme = {
palette: {
type: "light",
}
}
export function CustomThemeProvider({ children }) {
const [dark, setDark] = React.useState(false);
function toggleTheme() {
if (dark === true) {
setDark(false);
} else {
setDark(true);
}
}
const theme = React.useMemo(
() => {
if (dark === true) {
return createTheme(darkTheme);
}
return createTheme(lightTheme);
},
[dark],
);
return (
<CustomThemeContext.Provider value={toggleTheme}>
<ThemeProvider theme={theme}>
<CssBaseline />
{children}
</ThemeProvider>
</CustomThemeContext.Provider>
);
}
export function useToggleTheme() {
const context = useContext(CustomThemeContext);
if (context === undefined) {
throw new Error("useCustomThemeContext must be used within an CustomThemeProvider");
}
return context;
}
Then wrap your app in that:
ReactDOM.render(
<CustomThemeProvider>
<App />
</CustomThemeProvider>,
document.getElementById("app")
);
And then access it in your app:
export default function App(){
const toggleTheme = useToggleTheme();
return (
<div>
<button onClick={toggleTheme}>Toggle the theme!!</button>
</div>
);
}
On a side note, in my app, I actually have a different theme in two sections of the app, based on whether the user is logged in or not. So I'm just doing this:
function App() {
const { authState } = useAuthContext();
const theme = React.useMemo(
() => {
if (authState.user) {
return createTheme(dashboardTheme);
}
return createTheme(loginTheme);
},
[authState.user],
);
return (
<ThemeProvider theme={theme}>
<TheRestOfTheApp />
</ThemeProvider>
}
It seems you can base the theme off any piece of state, or multiple pieces, by referencing them in useMemo and including them in the dependency array.
EDIT:
I just noticed that MUI v5 actually has something very similar in their docs.
In your code, theme type is changed. But the Page is not re-rendered with new theme.
I have changed code in index.js and App.js like following.
Try this approach. It works.
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
ReactDOM.render(
<App/>,
document.getElementById("app")
);
App.js
import React from "react";
import CssBaseline from "#material-ui/core/CssBaseline";
import Typography from "#material-ui/core/Typography";
import { Button } from "#material-ui/core";
import { MuiThemeProvider, createMuiTheme } from "#material-ui/core/styles";
import blueGrey from "#material-ui/core/colors/blueGrey";
import lightGreen from "#material-ui/core/colors/lightGreen";
class App extends React.Component {
constructor(props){
super(props);
this.state = {
themeType : 'dark',
}
}
changeTheme(){
if (this.state.themeType == 'dark'){
this.setState({themeType:'light'});
} else {
this.setState({themeType:'dark'});
}
}
render() {
let theme = createMuiTheme({
palette: {
primary: {
light: lightGreen[300],
main: lightGreen[500],
dark: lightGreen[700]
},
secondary: {
light: blueGrey[300],
main: blueGrey[500],
dark: blueGrey[700]
},
type: this.state.themeType
}
});
return (
<MuiThemeProvider theme={theme}>
<CssBaseline />
<Typography>Hi there!</Typography>
<Button
variant="contained"
color="secondary"
onClick={()=>{this.changeTheme()}}
>
Change
</Button>
</MuiThemeProvider>
);
}
}
export default App;

Resources