The goal is to be able to access user data in any component upon the user logging in hence why <UserProvider>
is wrapped around <App/>
.
The issue’s that upon user successfully logging in, console.log("UserContext.js", res.data);
logs the user’s data only when refreshing the page.
I know this is due to the async stuff going on behind the scenes but I can’t put my finger on what’s exactly the problem.
How can I fix it?
Here’s UserContext.js
:
import { createContext, useContext, useState, useEffect } from 'react';
import ApiClient from "./ApiClient";
// Create the context
const UserContext = createContext();
// Custom hook to access the context
export const useUserContext = () => useContext(UserContext);
// Provider component to wrap the app and provide the context value
export const UserProvider = ({ children }) => {
const [userId, setUserId] = useState('');
const [name, setName] = useState('');
const headers = {
"Accept": 'application/json',
"Authorization": `Bearer ${localStorage.getItem('token')}`
};
const fetchUserData = () => {
ApiClient.get('get-user-data', {headers})
.then(res => {
console.log("UserContext.js", res.data);
setUserId(res.data.UserID);
setName(res.data.name);
}).catch(err => {
console.log(err);
});
}
useEffect(fetchUserData,[])
const contextValue = {
userId,
name,
refreshUserData: fetchUserData // provide this function in context so that it can be called elsewhere
};
return (
<UserContext.Provider value={contextValue}>{children}</UserContext.Provider>
);
};
Here’s Home.js
:
const { refreshUserData } = useUserContext();
useEffect(() => {
refreshUserData(); // first attempt
}, [theme]);
const handleLogin = (e) => {
e.preventDefault();
let dataLogin = {
'email': email,
'password': password
};
JSON.stringify(dataLogin);
ApiClient.post('/login', dataLogin)
.then(resp => {
refreshUserData();
history.push('/gallery'); // second attempt
}).catch(error => {
console.log(error);
});
};
Here’s App.js
:
import { UserProvider } from '../utilities/UserContext';
const App = () => {
return (
<Router>
<Switch>
<Route exact path="/" component={Home} />
<ProtectedRoute path="/gallery" component={Grid} />
<Route path="/support" component={Support} />
<Route path='/faq' component={FaqComp} />
</Switch>
</Router>
);
}
const container = document.getElementById('example');
const root = createRoot(container);
root.render(<UserProvider><App/></UserProvider>);
2
Answers
Move the declaration of
headers
insidefetchUserData
, so that setting thetoken
inlocalStorage
will affect the next fetch.use redux is the cleaner way to store global data for all components and you can only use the useSelector to access this global data every where and you can use the chrome storage to persist user informations
to reply for your issue try to use Context.Consumer
your UserProvider do too many unnecessary requests
the fetchUserData are called at the fisrt rendering even if the user is not logged in
every time a user change theme the refreshUserData are called and an http request are called for unchanged data (userId and name)