skip to Main Content

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


  1. Move the declaration of headers inside fetchUserData, so that setting the token in localStorage will affect the next fetch.

    Login or Signup to reply.
  2. 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

    <MyContext.Consumer>
      {value => /* render something based on the context value */}
    </MyContext.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)

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search