Search code examples
javascriptreactjsuse-statereact-functional-componentuse-context

React dark theme: setContext is not a function when accessing from useContext


I cannot seem to see what is going wrong here, pretty basic usage to useContext and useState hooks. I have a darkModeContext where I am literally just flipping the boolean for darkMode, but whilst trying to flip it for the context I am getting setContext is not a function.

I took some code out from the navDrawer to make it easier to see but here is the error I am getting along with the code: error in console - the boolean is flipped but cannot be passed to setDarkTheme withour erroring

DarkThemeContext.js

import React from "react";

const DarkThemeContext = React.createContext({
    darkMode: false,
    setDarkMode:() => {},
});

export default DarkThemeContext

DarkThemeSwitcher.js

import React, { useContext } from 'react';
import { IconButton, Tooltip } from '@material-ui/core';
import { makeStyles, useTheme } from '@material-ui/core/styles';
import LightThemeIcon from '@material-ui/icons/Brightness7';
import DarkThemeIcon from '@material-ui/icons/Brightness4';

import DarkThemeContext from '../DarkThemeContext/DarkThemeContext';

const DarkThemeSwitcher = () => {
    const useStyles = makeStyles((theme) => ({
        darkThemeButton: {
            background: "none",
            border: "none",
        },
        darkThemeMobileButton: {
            [theme.breakpoints.up('sm')]: {
                display: "none",
            },
            [theme.breakpoints.down('sm')]: {
                background: "none",
                border: "none",
                display: "visible",
                paddingLeft: theme.spacing(2),
                minWidth: '25%'

            },
        },
        menuHeaderText: {
            minWidth: '97%',
        }
    }));



    const classes = useStyles();
    const { darkMode, setDarkMode } = useContext(DarkThemeContext);

    const handleLightThemeToggle = () => {
        console.log(!darkMode)
        setDarkMode(!darkMode);
    };

    return (
        darkMode ?
            <Tooltip title="Light Mode" className={classes.darkThemeMobileButton} onClick={handleLightThemeToggle} >
                <IconButton aria-label="Light Mode">
                    <DarkThemeIcon />
                </IconButton>
            </Tooltip >
            :
            <Tooltip title="Dark Mode" className={classes.darkThemeMobileButton} onClick={handleLightThemeToggle} >
                <IconButton aria-label="Dark Mode">
                    <LightThemeIcon />
                </IconButton>
            </Tooltip>

    )
}

export default DarkThemeSwitcher;

NavDrawer.js

import React from 'react';
import DarkThemeContext from '../DarkThemeContext/DarkThemeContext';
import DarkThemeSwitcher from '../DarkThemeSwitcher/DarkThemeSwitcher';

const NavDrawer = ({ children, window }) => {
    const classes = useStyles();
    const theme = useTheme();
    const [mobileOpen, setMobileOpen] = React.useState(false);
    const [darkTheme, setDarkTheme] = React.useState(false);
    const value = { darkTheme, setDarkTheme };

const container = window !== undefined ? () => window().document.body : undefined;

    return (
        <DarkThemeContext.Provider value={value}>
            <ThemeProvider theme={darkThemeStyling}>
                <div className={classes.root}>
                    <CssBaseline />
                    <AppBar position="fixed" className={classes.appBar}>
                        <Toolbar>
                            <IconButton
                                color="inherit"
                                aria-label="open drawer"
                                edge="start"
                                onClick={handleDrawerToggle}
                                className={classes.menuButton}
                            >
                                <MenuIcon />
                            </IconButton>
                            <Typography className={classes.menuHeaderText} variant="h6" noWrap>
                                My app
                            </Typography>
                            <DarkThemeSwitcher/>
                        </Toolbar>
                    </AppBar>
                    <nav className={classes.drawer} aria-label="navigation links">
                        <Hidden smUp implementation="css">
                            <SwipeableDrawer
                                container={container}
                                variant="temporary"
                                anchor={theme.direction === 'rtl' ? 'right' : 'left'}
                                open={mobileOpen}
                                onClose={handleDrawerToggle}
                                classes={{
                                    paper: classes.drawerPaper,
                                }}
                                ModalProps={{
                                    keepMounted: true, // Better open performance on mobile.
                                }}
                            >
                                {drawer}
                            </SwipeableDrawer>
                        </Hidden>
                        <Hidden xsDown implementation="css">
                            <Drawer
                                classes={{
                                    paper: classes.drawerPaper,
                                }}
                                variant="permanent"
                                open
                            >
                                {drawer}
                            </Drawer>
                        </Hidden>
                    </nav>
                    <main className={classes.content}>
                        <div className={classes.toolbar} />
                        {children}
                        {console.log(value)}
                    </main>
                </div>
            </ThemeProvider>
        </DarkThemeContext.Provider>
    );
}
export default NavDrawer;

Solution

  • You have different keys in DarkThemeContext and in NavDrawer when you initialize the values, i.e. darkTheme vs darkMode.

    Rename in NavDrawer should resolve the error.

        // In NavDrawer
        const [darkMode, setDarkMode] = React.useState(false);
        const value = { darkMode, setDarkMode };