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
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.
console.log()inside the.catchofauthenticateUser()? Maybe there's an error in the request and you're not handling it. Also try to simply donav("/")instead of returning it.