I am trying to log out the user and then redirect to HomePage.
I have done the below signOut function from firebase 9.0 but the user comes as null.
Ideally would like to log out and remove session data or log out.
import React, { useContext, createContext, useEffect, useState } from 'react';
import { auth, db } from '../utils/init-firebase';
import {
confirmPasswordReset,
createUserWithEmailAndPassword,
GoogleAuthProvider,
onAuthStateChanged,
sendPasswordResetEmail,
signInWithEmailAndPassword,
signInWithGoogle,
signInWithPopup,
signOut,
} from 'firebase/auth';
import {
query,
collection,
onSnapshot,
forEach,
getDoc,
getDocs,
} from 'firebase/firestore';
const AuthContext = createContext({
// currentUser: null,
signInWithGoogle: () => Promise,
login: () => Promise,
register: () => Promise,
logout: () => Promise,
forgotPassword: () => Promise,
resetPassword: () => Promise,
});
export const useAuth = () => useContext(AuthContext);
export default function AuthContextProvider({ children }) {
const [currentUser, setCurrentUser] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const [subscription, setSubscription] = useState(null);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setCurrentUser(user ? user : null);
setIsLoading(false);
localStorage.setItem('user', true);
const uid = auth.currentUser?.uid;
});
});
});
return () => {
unsubscribe();
};
}, []);
const [uniqueId, setUniqueId] = useState(null);
useEffect(() => {
setUniqueId(currentUser);
}, []);
// console.log('user id from Auth Context==> ', currentUser);
function login(email, password) {
return signInWithEmailAndPassword(auth, email, password);
}
function logout() {
return signOut(auth);
}
const value = {
currentUser,
signInWithGoogle,
login,
register,
logout,
forgotPassword,
resetPassword,
isLoading,
subscription,
};
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
}
Related
AuthContext.js
import { createContext, useEffect, useState } from "react";
import { axiosInstance } from "../../axiosConfig";
import { useCustomToast } from "../../customHooks/useToast";
const initialState = {
user: null,
isLoggedIn: false,
login: () => null,
logOut: () => null,
};
export const AuthContext = createContext(initialState);
export const AuthContextProvider = ({ children }) => {
const [user, setUser] = useState(null);
const [isLoggedIn, setIsLoggedIn] = useState(false);
const { showToast } = useCustomToast();
console.log("i am rinning agaon here");
const checkLogin = async () => {
try {
const res = await axiosInstance.get("/auth/refresh");
setIsLoggedIn(true);
console.log("the user is", res?.data);
setUser(res?.data?.user);
} catch (e) {
console.log(e);
setIsLoggedIn(false);
}
};
const logOutHandler = async () => {
try {
const res = await axiosInstance.get("/auth/logout");
showToast(res?.data?.message);
} catch (e) {
showToast("Something went wrong.Please try again");
}
};
useEffect(() => {
checkLogin();
}, []);
const login = (userData) => {
setUser(userData);
setIsLoggedIn(true);
};
const logOut = () => {
setUser(null);
logOutHandler();
setIsLoggedIn(false);
};
return (
<AuthContext.Provider
value={{
user,
isLoggedIn,
login,
logOut,
}}
>
{children}
</AuthContext.Provider>
);
};
ProtectedRoute.js
import React, { useEffect, useContext } from "react";
import { useRouter } from "next/router";
import { AuthContext } from "../context/authContext";
const ProtectedRoute = ({ children }) => {
const { isLoggedIn } = useContext(AuthContext);
const router = useRouter();
useEffect(() => {
if (!isLoggedIn) {
router.push("/login");
}
}, [isLoggedIn]);
return <>{isLoggedIn && children}</>;
};
export default ProtectedRoute;
I am using NextJS and context api for managing user state. Here at first I will check for tokens and if it is valid I will set loggedIn state to true. But suppose I want to go to profile page which is wrapped by protected route, what is happening is AuthContext is resetting and evaluating itself from beginning, the isLoggedIn state is false when I go to /profile route. If I console log isLoggedIn state inside protectedRoute.js, it is false at start and before it becomes true, that router.push("/login) already runs before isLoggedIn becomes true. It feels like all AuthContext is executing again and again on each route change. Is there any code problem? How can I fix it? The one solution I have found is wrapping that wrapping that if(!loggedIn) statement with setTimeOut() of 1 secs so that until that time loggedIn becomes true from context API
I have used the context in other places, such as login, database functions, and more. However, when I try to run functions or variables inside my context in places such as custom api's or getServerSideProps, it returns the following error, TypeError: Cannot read properties of null (reading 'useContext'). I am attaching my auth context, my initialization of the context, and the getServerSideProps function that is returning an error
_app.js
import RootLayout from '../components/Layout'
import { AuthProvider } from '../configs/auth-context'
import '../styles/globals.css'
export default function App({ Component, pageProps }) {
return (
<AuthProvider >
<RootLayout>
<Component {...pageProps} />
</RootLayout>
</AuthProvider>
)}
auth-context
import React, { useContext, useState, useEffect, useRef } from 'react'
import { auth, db, provider } from './firebase-config'
import { GoogleAuthProvider, signInWithEmailAndPassword, createUserWithEmailAndPassword, signOut, onAuthStateChanged, signInWithPopup } from 'firebase/auth'
import { doc, getDoc, setDoc } from 'firebase/firestore'
import {useRouter} from 'next/router';
const AuthContext = React.createContext({currentUser: {uid: "TestUid", email:"Testeremail#email.com"}})
export function UseAuth() {
return useContext(AuthContext)
}
export function AuthProvider({ children }) {
const router = useRouter();
const [currentUser, setCurrentUser] = useState({uid: "TestUid", email:"Testeremail#email.com"})
const [loading, setLoading] = useState(true)
async function signup(email, password) {
createUserWithEmailAndPassword(auth, email, password)
.then(async (result) => {
const user = result.user;
await userToDb(user);
router.push('/portfolio');
return user;
}).catch((error) => {
console.error(error);
})
return
}
async function login(email, password) {
return signInWithEmailAndPassword(auth, email, password)
.then(async (result) => {
const user = result.user;
await userToDb(user);
router.push('/portfolio');
return user;
}).catch((error) => {
console.error(error)
})
}
function logout() {
router.push('/')
return signOut(auth)
}
async function googleSignIn() {
const provider = new GoogleAuthProvider();
signInWithPopup(auth, provider)
.then(async (result) => {
const credential = GoogleAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
// The signed-in user info.
const user = result.user;
await userToDb(user);
router.push('/portfolio');
return user
}).catch((error) => {
console.log(error)
// const errorCode = error.code;
// const errorMessage = error.message;
// The email of the user's account used.
// const email = error.customData.email;
// The AuthCredential type that was used.
// const credential = GoogleAuthProvider.credentialFromError(error);
} )
}
const userToDb = async (user) => {
// await setDoc(doc(db, "users", user.uid), {
// userEmail: user.email,
// userID: user.uid
// }, {merge: false})
let currentRef = doc(db, 'users', user.uid)
let currentUserID = user.uid;
let currentEmail = user.email;
await setDoc(currentRef, {
userEmail: currentEmail,
userID: currentUserID
}, {merge: false})
}
function fixData(docs) {
console.log("this works")
// setDocuments(docs);
let retMap = new Map();
if (currentUser !== null) {
docs?.map(function(doc) {
console.log(doc)
let tic = doc.stockTicker
let data = {
shares: doc.shares,
price: doc.price,
type: doc.type
}
if(!retMap.has(tic)) {
retMap.set(tic, [data]);
console.log(tic + " " + data)
// setMap(new Map(datamap.set(tic, {shares: shares, averagePrice: price})))
}
else {
let x = retMap.get(tic);
x.push(data);
}
})
console.log(retMap)
return retMap;
}
}
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, async user => {
setCurrentUser(user)
setLoading(false)
})
return unsubscribe
}, [])
const value = {
currentUser,
login,
signup,
logout,
googleSignIn,
fixData
}
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
)
}
getServerSideProps
export async function getServerSideProps() {
let allDocs = []
let avgDocs = []
const {currentUser} = UseAuth()
return {
props: {allDocs, avgDocs}
}
}
I don't know the correct answer, but hooks should be used in components and hooks without exception to ssr.
I'm having this problem with my AuthContext from a React app. I check the localStorage and the values stored are from the last user logged, so I have to login twice to get the corrent info.
This is the Context code. And the 'useLocalStorage' function. I guess in the useMemo is the problem but I haven't been able to solve it. Is this a bad approach?
import { createContext, useContext, useMemo, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useLocalStorage } from "./useLocalStorage";
import jwtDecode from "jwt-decode";
const AuthContext = createContext();
export const AuthProvider = ({ children }) => {
const [token, setToken] = useLocalStorage("accessToken", null);
const [userId, setUserId] = useLocalStorage("currentUserId", null);
const [userEmail, setUserEmail] = useLocalStorage("currentUserEmail", null);
const [userRole, setUserRole] = useLocalStorage("currentUserRole", null);
const [user, setUser] = useState({});
const navigate = useNavigate();
const login = async (credentials) => {
return fetch("https://localhost:7264/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(credentials),
})
.then((res) => res.json())
.then((res) => {
setToken(res);
console.log(res);
const tokenToDecode = localStorage.getItem("accessToken");
setUser(jwtDecode(tokenToDecode));
setUserId(user.sub);
setUserEmail(
user[
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"
]
);
setUserRole(
user["http://schemas.microsoft.com/ws/2008/06/identity/claims/role"]
);
});
};
const logout = () => {
setToken(null);
setUserId(null);
setUserEmail(null);
setUserRole(null);
navigate("/", { replace: true });
};
const value = useMemo(
() => ({
token,
login,
logout,
userId,
userEmail,
userRole,
}),
[token]
);
return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
};
export const useAuth = () => {
return useContext(AuthContext);
};
import { useState } from "react";
export const useLocalStorage = (keyName, defaultValue) => {
const [storedValue, setStoredValue] = useState(() => {
try {
const value = localStorage.getItem(keyName);
if (value) {
return JSON.parse(value);
} else {
localStorage.setItem(keyName, JSON.stringify(defaultValue));
return defaultValue;
}
} catch (err) {
return defaultValue;
}
});
const setValue = (newValue) => {
try {
localStorage.setItem(keyName, JSON.stringify(newValue));
} catch (err) {}
setStoredValue(newValue);
};
return [storedValue, setValue];
};
I have the above firebase db. I want to extract the displayName value and use it in a greeting message after the user is successfully login (e.g. Hello George!). I manage to achieve this but when I refresh the page everything disappears and in console I get this error "index.esm2017.js:1032 Uncaught TypeError: Cannot read properties of undefined (reading 'indexOf')".
Is this a problem of how I extract the displayName from firebase document?
Can someone explain to me what is the problem, please?
Here is my code:
AuthContext.js
import { createContext, useContext, useEffect, useState } from "react";
import {
onAuthStateChanged,
signInWithEmailAndPassword,
signOut,
} from "firebase/auth";
import { auth } from "../utils/firebase/firebase.utils";
const UserContext = createContext();
export const AuthContextProvider = ({ children }) => {
const [user, setUser] = useState({});
const signIn = (email, password) =>
signInWithEmailAndPassword(auth, email, password);
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (currentUser) => {
console.log(currentUser);
setUser(currentUser);
});
return () => unsubscribe();
}, []);
const logOut = () => signOut(auth);
return (
<UserContext.Provider value={{ user, signIn, logOut }}>
{children}
</UserContext.Provider>
);
};
export const UserAuth = () => {
return useContext(UserContext);
};
WelcomePage.jsx
import React, { useState, useEffect } from "react";
import { UserAuth } from "../contexts/AuthContext";
import { db } from "../utils/firebase/firebase.utils";
import { doc, Firestore, getDoc } from "firebase/firestore";
const WelcomePage = () => {
const [userDetails, setUserDetails] = useState({});
const { user } = UserAuth();
useEffect(() => {
const docRef = doc(db, "users", user.uid);
const fetchData = async () => {
try {
const docSnap = await getDoc(docRef);
setUserDetails(docSnap.data());
console.log(docSnap.data());
} catch (e) {
console.log(e);
}
};
fetchData();
}, [user]);
return (
<div>
<h1>Hello, {userDetails.displayName}!</h1>
</div>
);
};
export default WelcomePage;
You might only want to fetch documents in the WelcomePage component if there's a truthy uid value to use.
const { user } = UserAuth();
useEffect(() => {
const fetchData = async () => {
const docRef = doc(db, "users", user.uid);
try {
const docSnap = await getDoc(docRef);
setUserDetails(docSnap.data());
console.log(docSnap.data());
} catch (e) {
console.log(e);
}
};
if (user?.id) {
fetchData();
}
}, [user]);
I want to know that how can we save user data during signup in Firestore.
import {
createUserWithEmailAndPassword,
onAuthStateChanged,
signInWithEmailAndPassword,
signOut,
} from "firebase/auth";
import { collection, setDoc, doc } from "firebase/firestore";
import React, { useContext, useEffect, useState } from "react";
import { auth, db } from "../firebaseConfig";
const AuthContext = React.createContext();
export const useAuth = () => {
return useContext(AuthContext);
};
export const AuthProvider = ({ children }) => {
const [currentUser, setCurrentUser] = useState();
const [loading, setLoading] = useState(true);
const signup = async (email, password) => {
return createUserWithEmailAndPassword(auth, email, password).then(
(cred) => {
setDoc(doc(collection(db, "users", cred.user.uid)), {
name: "manan",
city: "chd",
});
}
);
};
const login = (email, password) => {
return signInWithEmailAndPassword(auth, email, password);
};
const logout = () => {
return signOut(auth);
};
useEffect(() => {
const unsubscribe = onAuthStateChanged(auth, (user) => {
setCurrentUser(user);
setLoading(false);
});
return unsubscribe;
}, []);
const value = {
currentUser,
login,
signup,
logout,
};
return (
<AuthContext.Provider value={value}>
{!loading && children}
</AuthContext.Provider>
);
};
as you can see I have created an AuthContext.js for all the functions. now, when in signup() I am not able to store the user data corresponding to their UID... as I have used
setDoc(doc(collection(db, "users", cred.user.uid)), {
name: "manan",
city: "chd",
});
but it is not working at all it's throwing an error
error:
Objects are not valid as a React child (found: object with keys {error}). If you meant to render a collection of children, use an array instead.
you are using both a document and a collection as a reference. you should only use a document reference on a setDoc
from:
setDoc(doc(collection(db, "users", cred.user.uid)), {
name: "manan",
city: "chd",
});
change to:
setDoc(doc(db, "users", cred.user.uid), {
name: "manan",
city: "chd",
});