I'm trying to figure out how to change global state from a componenet using useContext
and useReducer
dispatch method.
The component is simply should change the backgournd of the page on a click
Here is how I defined the context ThemeContext.js
import { createContext, useReducer } from "react";
import ThemeReducer from './ThemeReducer'
const INITIAL_STATE = {
isLightTheme: true,
light: {syntax: '#555', ui: '#ddd', bg: '#eee'},
dark: {syntax: '#ddd', ui: '#333', bg: '#555'},
}
export const ThemeContext = createContext(INITIAL_STATE);
const ThemeContextProvider = ({ children }) => {
const [state, dispatch] = useReducer(ThemeReducer, INITIAL_STATE);
return (
<ThemeContext.Provider value={{
isLightTheme: state.isLightTheme,
light: state.light,
dark: state.dark,
dispatch,
}}>
{children}
</ThemeContext.Provider>
);
}
export default ThemeContextProvider;
The ThemeReducer.js
is:
const ThemeReducer = (state, action) => {
switch (action.type) {
case "SET_DARK":
return {
isLightTheme: false,
};
case "SET_LIGHT":
return {
isLightTheme: true,
};
default:
return state;
}
};
export default ThemeReducer;
app.js:
function App() {
return (
<div className="App">
<ThemeContextProvider>
<Navbar />
<BookList />
<ThemeToggle />
</ThemeContextProvider>
</div>
);
}
export default App;
And the ThemeToggle.js
compoenent
const ThemeToggle = () => {
return (
<button onClick={()=>dispatch({type: "SET_DARK"})}>Change theme</button>
);
}
export default ThemeToggle;
However I get this error:
src/components/ThemeToggle.jsx
Line 6:30: 'dispatch' is not defined
I don't understand why. Because dispatch
is supposed to be in the context. I'm wondering what is wrong here and how can I fix it?
P.S BooKList
compoenent.
import { useContext } from 'react'
import { ThemeContext } from '../context/ThemeContext';
const BookList = () => {
const {isLightTheme, light, dark} = useContext(ThemeContext)
const theme = isLightTheme ? light : dark;
return (
<div style={{background : theme.ui , color: theme.syntax}}>
<ul>
<li stryle={{background: theme.ui}} >The way of kings</li>
<li stryle={{background: theme.ui}} >The namoe fot the wind</li>
<li stryle={{background: theme.ui}} >The Final empire</li>
</ul>
</div>
);
}
It appears you are missing accessing the ThemeContext
in ThemeToggle
. Use the useContext
hook to access the ThemeContext
Context value and destructure the dispatch
function.
const ThemeToggle = () => {
const { dispatch } = useContext(ThemeContext);
return (
<button onClick={() => dispatch({type: "SET_DARK"})}>
Change theme
</button>
);
}
export default ThemeToggle;
And for completeness' sake, add dispatch
to the default context value.
const INITIAL_STATE = {
isLightTheme: true,
light: {syntax: '#555', ui: '#ddd', bg: '#eee'},
dark: {syntax: '#ddd', ui: '#333', bg: '#555'},
dispatch: () => {},
}
export const ThemeContext = createContext(INITIAL_STATE);
The ThemeReducer
reducer function is stripping out state in the set light/dark cases. You need to preserve the existing state.
const ThemeReducer = (state, action) => {
switch (action.type) {
case "SET_DARK":
return {
...state,
isLightTheme: false,
};
case "SET_LIGHT":
return {
...state,
isLightTheme: true,
};
default:
return state;
}
};