I am trying to use the react context api. I am experiencing an issue though where the context/state value is not updating. I have no idea why this is happening and have looked at numerous threads but have found that nothing works for me.
Here is the code:
curtain-context.js
For creating the contexts and exporting them:
const CurtainContext = createContext({
curtainVisible: false,
setCurtainVisible: (value) => {}
});
export function CurtainContextProvider(props) {
const [curtainVisible, setCurtainVisible] = useState();
function setCurtainVisibleHandler(value) {
setCurtainVisible(value);
console.log(value);
}
const context = {
curtainVisible: curtainVisible,
setCurtainVisible: setCurtainVisibleHandler
};
return (
<CurtainContext.Provider value={context}>
{props.children}
</CurtainContext.Provider>
);
}
export default CurtainContext;
App.js
The main application code which is surrounded by the context provider:
<Layout>
<CurtainContextProvider>
<Routes>
<Route element={<HomePage/>} path='/' exact/>
<Route element={<HomePage/>} path='/home' exact/>
<Route element={<ServicesPage/>} path='/services' exact/>
<Route element={<ProductsPage/>} path='/products' exact/>
<Route element={<ContactPage/>} path='/contact' exact/>
<Route element={<LoginPage/>} path='/login' exact/>
</Routes>
</CurtainContextProvider>
</Layout>
MainNavigation.js
The place where I want to use the context value to render something if curtainVisible is true:
import { NavLink } from 'react-router-dom';
import classes from './MainNavigation.module.css';
import React, {useContext, useState} from "react";
import { useLocation } from "react-router";
import MobileCurtain from "../ui/MobileCurtain";
import CurtainContext from "../../store/curtain-context";
function MainNavigation() {
var curtainContext = useContext(CurtainContext);
const { pathname } = useLocation();
const activeClass = ({isActive}) => (isActive ? classes.active : classes.inactive);
const activeUserClass = ({paths = ['/login', '/settings']}) => (paths.includes(pathname) ? classes.active : classes.inactive);
function handleBarsClicked() {
curtainContext.setCurtainVisible(true);
}
return (
<div className={classes.menu}>
<ul>
<li className={classes.textLinkBars}><button className={classes.iconButton} onClick={handleBarsClicked}><FontAwesomeIcon icon={faBars} className={classes.bars}/></button></li>
{ curtainContext.curtainVisible ? <MobileCurtain/> : null}
<li className={classes.textLink}><NavLink to="/" className={activeClass}>Home</NavLink></li>
<li className={classes.textLink}><NavLink to="/services" className={activeClass}>Services</NavLink></li>
<li className={classes.textLink}><NavLink to="/products" className={activeClass}>Products</NavLink></li>
<li className={classes.textLink}><NavLink to="/contact" className={activeClass}>Contact</NavLink></li>
</div>
</ul>
</div>
);
}
export default MainNavigation;
CodePudding user response:
Only components that are descendants of the Provider
can use context value.
In your example, MainNavigation
isn't a descendant of CurtainContextProvider
hence the issue.
You set your initial value to
{
curtainVisible: false,
setCurtainVisible: (value) => {}
}
which didn't helped, because this (value) => {}
was run instead of setCurtainVisibleHandler
.
I would suggest using undefined
as an initial value of context
Also, hooks like this can help prevent the issue like yours:
const useCurtainContext = () => {
const context = useContext(CurtainContext);
if (!context) {
throw new Error('`useCurtainContext` have to be used inside `CurtainContextProvider`')
}
return context
}