Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

React context doesn't update value when updated

I’m trying to make a simple routing system. Either the user is authenticated or not. I’m trying to use hooks to achieve this but I’m not having the biggest success with it just yet.

authProvider.tsx

import React, {Dispatch, PropsWithChildren, SetStateAction, useContext, useState} from "react";
import { signInWithEmailAndPassword } from "firebase/auth";
import {ref, child, get} from "firebase/database";
import { database, auth } from "../data/firebase";
import { useNavigate } from "react-router-dom";
import { Admin } from "../types/user.type";

interface AuthContext {
    user: Admin | undefined;
    setUser: Dispatch<SetStateAction<Admin>>;
    authenticateUser: (email: string, password: string) => void;
    authenticated: boolean;
    setAuthenticated: Dispatch<SetStateAction<boolean>>;
}
const AuthContext = React.createContext<AuthContext>({} as AuthContext)

export const useAuth = () => useContext(AuthContext)

export const AuthProvider = ({children}: PropsWithChildren<{}>) => {
    const [user, setUser] = useState<Admin>({} as Admin)
    const [authenticated, setAuthenticated] = useState<boolean>(false)
    const nav = useNavigate();

    const authenticateUser = (email: string, password: string):void=> {

        signInWithEmailAndPassword(auth, email, password)
            .then(async (userCredential) => {
                // Signed in
                const admin =  await fetchUserData(userCredential.user.uid)
                console.log(admin)
                if (admin?.role !== "Admin") return;
                setUser(user);
                setAuthenticated(true)
                return nav("/")
            })
            .catch((error) => {
                const errorCode = error.code;
                const errorMessage = error.message;
            });

    }

    const fetchUserData = (uid: string): Promise<Admin | null> => {
        return new Promise((resolve, reject) => {
            const dbRef = ref(database);

            get(child(dbRef, `users/${uid}`))
                .then((snapshot) => {
                    if (snapshot.exists()) {
                        resolve(snapshot.val() as Admin) ;
                          console.log(snapshot.val());
                    } else {
                        reject("Admin not found.")
                        //   console.log("No data available");
                    }
                })
                .catch((error) => {
                    reject("There was an error fetching the admin data.")
                });
        })
    }


    return (
        <AuthContext.Provider value={{ user, setUser, authenticated, setAuthenticated, authenticateUser }}>
            {children}
        </AuthContext.Provider>
    )
}

App.tsx

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

import React from "react";
import {Route, Routes, Navigate,BrowserRouter} from "react-router-dom";
import { Home, Login } from "./pages";
import { AuthProvider, useAuth } from "./providers/authProvider";

function App() {
    const {authenticated} = useAuth()
    return (
        <BrowserRouter>
            <AuthProvider>
                {authenticated
                    ? (
                        <Routes>
                            <Route path="/" element={<Home />}/>
                        </Routes>
                    )
                    :(
                        <Routes>
                            <Route path="/login" element={<Login />}/>
                            <Route path="*" element={<Navigate to="/login" />}/>
                        </Routes>
                    )
                }
            </AuthProvider>
        </BrowserRouter>
    );
}

export default App;

I call authenticateUser from my login page. The function gets called as expected and the user object is logged in the console. Have I missed something in the documentation which led to authenticated not being updated in the App.tsx? It also doesn’t refresh the page when calling nav("/") after setting authenticated to true.

>Solution :

You are calling the useAuth() Hook which consumes the AuthContext outside of the actual context. In order to get the data from AuthContext you have to wrap it around <App /> in that case.

const auth = useAuth(); // outside of your authprovider

return (
    <AuthProvider> {/* your context definition component */}
        {/* You can only access the context INSIDE AuthProvider */}
        <ChildrenComponent />
        <ChildrenComponent />
        <ChildrenComponent />
        <ChildrenComponent />
    </AuthProvider>
)

do this in your index.tsx or whereever you call your app.tsx:

<AuthProvider>
    <App /> {/* useAuth() has now access to the context */}
</AuthProvider>
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading