Reputation: 1356
I am unable to set context state in my app.js, I get empty values in it somehow, but can access it in child component.
I want to set context state in app.js whenever user comes to page so that I can use it throughout the application, for example show different headers based on whether user is logged in or not
SandBox URL as requested -> https://codesandbox.io/s/quizzical-snyder-2qghj?file=/src/App.js
I am following https://upmostly.com/tutorials/how-to-use-the-usecontext-hook-in-react
app.js
// import installed dependencies
import React, { useEffect, useContext } from 'react';
import { BrowserRouter as Router, Switch, Route } from 'react-router-dom';
// import custom contexts
import { AuthContext, AuthContextProvider } from './contexts/auth/AuthContext';
// import pages
import Homepage from './pages/homepage/Homepage';
// import components
import Footer from './components/footer/Footer';
import Header from './components/header/Header';
export default function App() {
const [authState, setAuthState] = useContext(AuthContext);
useEffect(() => {
console.log(authState); // prints *{}*
console.log(setAuthState); // prints *() => {}*
const token = localStorage.getItem('token');
const tokenIsExpired = parseInt(localStorage.getItem('tokenIsExpired'));
if (!tokenIsExpired && token.length) {
setAuthState({
userIsLoggedin: true,
fName: 'test fname',
lName: 'test lname',
userName: 'testname'
});
} else {
setAuthState({
userIsLoggedin: false,
fName: '',
lName: '',
userName: ''
});
}
if (tokenIsExpired) {
localStorage.setItem('token', '');
}
}, [authState, setAuthState]);
return (
<Router>
<AuthContextProvider value={[authState, setAuthState]}>
<div className='App'>
<Header />
<Switch>
<Route exact path='/'>
<Homepage />
</Route>
</Switch>
<Footer />
</div>
</AuthContextProvider>
</Router>
);
}
AuthContext.js
import React, { useState, createContext } from 'react';
const AuthContext = createContext([{}, () => {}]);
const AuthContextProvider = (props) => {
const [authState, setAuthState] = useState({
userIsLoggedin: false,
fName: '',
lName: '',
userName: ''
});
return (
<AuthContext.Provider value={[authState, setAuthState]}>
{props.children}
</AuthContext.Provider>
);
};
export { AuthContext, AuthContextProvider };
UseAuthCOntext.js
import { useContext } from 'react';
import { AuthContext } from './AuthContext';
const useAuthContext = () => {
const [authState, setAuthState] = useContext(AuthContext);
const login = (loginDetails) => {
setAuthState({
userIsLoggedin: true,
fName: 'test fname',
lName: 'test lname',
userName: 'testname'
});
};
const logout = () => {
setAuthState({
userIsLoggedin: false,
fName: '',
lName: '',
userName: ''
});
};
return { login, logout };
};
export default useAuthContext;
Header.js
// import installed dependencies
import React, { useContext, useEffect } from 'react';
// import components
import LoggedOutHeader from './logged-out-header/LoggedOutHeader';
import LoggedInHeader from './logged-in-header/LoggedInHeader';
// import custom contexts
import { AuthContext } from '../../contexts/auth/AuthContext';
const Header = () => {
const [authState, setAuthState] = useContext(AuthContext);
console.log(authState); //prints *{userIsLoggedin: false, fName: "", lName: "", userName: ""}*
console.log(setAuthState); //prints *ƒ dispatchAction(fiber, queue, action) {...*
const header = authState.isUserLoggedIn ? (
<LoggedInHeader />
) : (
<LoggedOutHeader />
);
return header;
};
export default Header;
Upvotes: 2
Views: 8874
Reputation: 755
You could use the context provider inside index.js
.
ReactDOM.render(
<AuthContextProvider>
<App />
</AuthContextProvider>,
document.getElementById('root')
)
Upvotes: 8
Reputation: 752
Your are using the context in the App component, which is not wrapped within AuthContextProvider. In that case the useContext call in the App component will not return the value provided to AuthContextProvider but instead it'll return the "default" values provided to the createContext call.
You need to defer those logic in the App component to a children component within AuthContextProvider.
See note from createContext api: The defaultValue argument is only used when a component does not have a matching Provider above it in the tree. This can be helpful for testing components in isolation without wrapping them. Note: passing undefined as a Provider value does not cause consuming components to use defaultValue.
Upvotes: 1
Reputation: 53874
You are passing value
to AuthContextProvider
, which seems like the value
you want to use, and you don't use it.
// value not used inside `AuthContextProvider`
<AuthContextProvider value={[authState, setAuthState]}>
It should be:
const AuthContextProvider = (props) => {
return (
<AuthContext.Provider value={props.value}>
{props.children}
</AuthContext.Provider>
);
};
Upvotes: 0