'dispatch' is not defined when using useReducer with useContext in react

Viewed 422

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>
     );
}
1 Answers

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;
  }
};  
Related