我对反应编码非常陌生,我遇到了一个无法解决的问题。
问题是,登录页面后将重定向到个人资料详细信息,登录时工作正常并显示个人资料详细信息,但当我刷新页面时,不会呈现个人资料详细信息。
function ProfileDetails() {
const {
profileData,
loading,
backendUrl,
token,
userId,
setProfileData,
setLoading,
} = useContext(AppContext);
const fetchProfileData = async () => {
try {
setLoading(true); // Start loading
const { data } = await axios.get(`${backendUrl}/api/user/profile/${userId}`, {
headers: {
Authorization: `Bearer ${token}`,
},
});
if (data.success) {
setProfileData(data.user); // Set profile data in context
}
} catch (err) {
toast.error("Error fetching the profile data");
console.error("Error fetching the data", err);
} finally {
setLoading(false); // Stop loading
console.log("profileData",profileData)
}
};
useEffect(()=>{
if(token && userId){
fetchProfileData()
}
},[token,userId])
if (loading) {
return (
<div className="flex justify-center items-center h-screen">
<CircularProgress />
</div>
);
}
if (!profileData) {
return <div>No profile data available</div>;
}
return profileData && (
<div
id="candidate_header"
className="flex flex-col md:flex-row md:items-end bg-[#f5f7fd] gap-10 md:justify-between pt-20 pb-8 px-5"
>
<div id="skills" className="flex flex-wrap gap-2 justify-center md:justify-normal">
{profileData.skills.map((skill, index) => (
<p key={index} className="skill-candidatedetails">{skill}</p>
))}
</div>
<div id="image" className="w-full flex flex-col items-center md:w-1/3">
<div className="w-16 h-16 bg-gray-100 rounded-full overflow-hidden cursor-pointer">
<img
className="w-full h-full object-cover"
src={profileData.profileImg}
alt="Profile photo"
/>
</div>
<h2 className="text-2xl font-bold my-1 text-center">{profileData.name}</h2>
<h3 className="text-sm font-normal text-[#1967D2] text-center">
{profileData.designation}
</h3>
</div>
<div className="md:w-1/3 w-full flex justify-center">
<button className="ui-btn">Download CV</button>
<button className="ui-btn">Invite</button>
</div>
</div>
);
}
下面是登录组件代码
// LoginPage.js
import React, { useContext, useState, useEffect } from "react";
import Logo from "./../images/logo.svg";
import axios from "axios";
import { AppContext } from "../AppContext";
import { toast } from "react-toastify";
import { useNavigate } from "react-router-dom";
function LoginPage() {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const { backendUrl, setUserId, setToken,setProfileData,profileData } =
useContext(AppContext);
const navigate = useNavigate();
const loginHandler = async (e) => {
e.preventDefault();
try {
const { data } = await axios.post(`${backendUrl}/api/user/login`, {
email,
password,
});
if (data.success) {
localStorage.setItem("token", data.token);
setToken(data.token);
axios.defaults.headers.common['Authorization'] = `Bearer ${data.token}`;
setUserId(data.user._id)
setProfileData(data.user)
toast.success("Login Successful");
navigate(`/profile/${data.user._id}`);
}
} catch (err) {
const errorMessage =
err.response?.data?.message || "An error occurred while logging in";
toast.error(errorMessage);
console.log(errorMessage);
}
};
return (
<form className="min-h-[80vh] flex items-center" onSubmit={loginHandler}>
<div className="flex flex-col gap-3 m-auto items-start p-8 min-w-[340px] sm:min-w-96 border rounded-xl text-[#5E5E5E] text-sm shadow-lg">
<img src={Logo} className="mx-auto" />
<div className="w-full">
<p>Email</p>
<input
className="border border-[#DADADA] rounded w-full p-2 mt-1"
value={email}
type="email"
onChange={(e) => setEmail(e.target.value)}
required
/>
</div>
<div className="w-full">
<p>Password</p>
<input
className="border border-[#DADADA] rounded w-full p-2 mt-1"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</div>
<button className="bg-[#1967D2] text-white w-full py-2 rounded-md text-base">
Login
</button>
</div>
</form>
);
}
export default LoginPage;
AppContext代码
const AppContextProvider = (props) => {
const [token, setToken] = useState(localStorage.getItem("token") || "");
const [userId, setUserId] = useState("");
const [profileData, setProfileData] = useState("");
const [loading, setLoading] = useState(false); // Add loading state
const backendUrl = import.meta.env.VITE_BACKEND_URL;
const value = {
backendUrl,
setToken,
userId,
setUserId,
loading,
profileData,
setProfileData
};
return (
<AppContext.Provider value={value}>{props.children}</AppContext.Provider>
);
};
我认为问题在于 Profiledata 状态变量在刷新时不保存数据,但如果令牌可用,我正在运行 fetchprofileData 函数。
ProfileDetails组件之所以在刷新页面时没有渲染用户的详细信息,很大程度上是因为这里的这段代码:
useEffect(()=>{
if(token && userId){
fetchProfileData()
}
},[token, userId])
此 useEffect 挂钩的目的是在组件安装时运行其中的代码。这个 useEffect 挂钩会在您登录时运行,因为您正在
loginHandler
函数中设置 userId 的值和令牌状态。但是当您刷新页面时,令牌和 userId 的状态都会丢失。但是,只有令牌保留在本地存储中。我看到您正在从 AppContextProvider 的本地存储中检索它
const [token, setToken] = useState(localStorage.getItem("token") || "");
在这种情况下,token 状态不为空,但 userId 状态为空。 useEffect 中的检查意味着令牌和 userId 状态都必须具有真实值,然后才能运行
fetchProfileData
函数。
为了解决此问题,您可能必须从 useEffect 挂钩中删除 userId 条件,然后仅检查令牌:
useEffect(()=>{
if(token){
fetchProfileData()
}
},[token])
但是,如果存在 userId 对于 useEffect 挂钩运行很重要,那么您可能希望将 userId 值保留在本地存储中,就像您对令牌所做的那样:
const loginHandler = async (e) => {
e.preventDefault();
try {
const { data } = await axios.post(`${backendUrl}/api/user/login`, {
email,
password,
});
if (data.success) {
localStorage.setItem("token", data.token);
localStorage.setItem("userId", data.user._id);
setToken(data.token);
axios.defaults.headers.common['Authorization'] = `Bearer ${data.token}`;
setUserId(data.user._id)
setProfileData(data.user)
toast.success("Login Successful");
navigate(`/profile/${data.user._id}`);
}
} catch (err) {
const errorMessage =
err.response?.data?.message || "An error occurred while logging in";
toast.error(errorMessage);
console.log(errorMessage);
}
};
然后您可以在 AppContextProvider 中检索它:
const [userId, setUserId] = useState(localStorage.getItem("userId") || "");
我希望这有助于解决您的问题。