i created a login page which will post request on the /auth/login
route in my backend express server after my backend server verifies the user the page will navigate on this route /auth/profile/${userId}
using useNavigate, this route will get the user information from the database the URL is showing that I am getting the id from backend but when I am using context API to fetch user details it is showing that the userId is undefined in the route how I get the user id first to get the request in my context API
loginUser function
const loginUser = async (e) => {
e.preventDefault();
const { email, password } = login;
try {
const response = await axios.post('/auth/login', {
email,
password,
});
const { userId } = response.data;
setUserId(userId);
console.log(`hi i am login page the user id is ${userId}`);
if (login.error) {
toast.error(login.error);
} else {
setLogin({});
toast.success('Login Successful. Welcome!');
// Use the updated userId here
navigate(`/auth/profile/${userId}`);
}
} catch (error) {
console.error(error);
}
};
contextApi
import React, {useState, useEffect} from "react";
import axios from "axios"
import { createContext } from "react";
export const UserContext = createContext({});
export function UseContextProvider({children , userId}) {
const [user, setUser] = useState(null);
useEffect(() => {
const fetchUserDetails = async () => {
try {
const response = await axios.get(`/auth/profile/${userId}`)
setUser(response.data);
} catch (error) {
console.error("Error fetching user details:", error);
}
}
fetchUserDetails();
}, [userId])
return (
<UserContext.Provider value={{ user, setUser }}>
{children}
</UserContext.Provider>
);
};
Backend -- loginUser Function
const loginUser = async (req, res) => {
try {
const { email, password } = req.body;
if (!email || !password) {
return res.json({ message: 'Please enter all the details' })
}
//check if the user already exist or not
const userExist = await User.findOne({ email: req.body.email });
if (!userExist) {
return res.json({ message: 'Wrong credentials' })
}
// check password match
const isPasswordMatched = await comparePassword(password, userExist.password);
if (!isPasswordMatched) {
return res.json({ message: 'Wrong credentials pass' });
}
const token = await jwt.sign({ id: userExist._id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRE,
})
return res
.cookie('token', token, { httpOnly: true })
.json({ success: true, message: 'LoggedIn Successfully', userId: userExist._id });
} catch (error) {
return res.json({ error: error });
}
};
getProfile function
const getProfile = async (req, res) => {
try {
const user = await User.findOne();
if (!user) {
return res.json({ message: 'No user found' })
}
return res.json({ user: user})
} catch (error) {
return res.json({ error: error });
}
};
isAuthenticated Middleware
const isAuthenticated = async (req, res, next) => {
try {
const token = req.headers.authorization;
if (!token) {
return res.status(401).json({ message: 'Unauthorized: No token provided' });
}
jwt.verify(token, process.env.JWT_SECRET, async (err, decoded) => {
if (err) {
// Handle token verification failure
return res.status(401).json({ message: 'Unauthorized: Invalid token' });
}
// Check if the token has expired
if (decoded.exp < Date.now() / 1000) {
return res.status(401).json({ message: 'Unauthorized: Token has expired' });
}
// Retrieve user information from the database using the user ID from the token
req.user = await User.findById(decoded.id);
next();
});
} catch (error) {
// Handle other errors
return next(error);
}
};
Maintain a state for userId
to initaite the useEffect
hook.
//context.jsx
export function UseContextProvider({children}) {
const [user, setUser] = useState(null);
const [userId, setUserId] = useState(null);
useEffect(() => {
const fetchUserDetails = async () => {
try {
const response = await axios.get(`/auth/profile/${userId}`)
setUser(response.data);
} catch (error) {
console.error("Error fetching user details:", error);
}
}
fetchUserDetails();
}, [userId])
return (
//pass the setuserId state and if needed also pass the userId
<UserContext.Provider value={{ user, userId, setUser, setUserId }}>
{children}
</UserContext.Provider>
);
};
Remove the separate state that is maintained in the LoginForm.jsx
, instead utilize the state from the context.jsx
.
//LoginForm.jsx
const Loginform = () => {
const navigate = useNavigate();
//remove this state
const { setUserId } = React.useContext(UserContext);
const [login, setLogin] = useState({
email: '',
password: '',
})
const loginUser = async (e) => {
e.preventDefault();
const { email, password } = login;
try {
const response = await axios.post('/auth/login', {
email,
password,
});
const { userId } = response.data;
setUserId(userId); //setUserId from context.jsx
if (login.error) {
toast.error(login.error);
} else {
setLogin({});
toast.success('Login Successful. Welcome!');
navigate(`/auth/profile/${userId}`);
}
} catch (error) {
console.error(error);
}
};
}
This will update the userId
and trigger the useEffect
in the context Provider to fetch the user details.