我尝试使用 Express 服务器和 PostgreSQL 数据库在 React 中开发一个 Web 应用程序,同时也使用 OpenWeatherMap API。
我在服务器上配置了基于 JWT 令牌的身份验证策略,并创建了各种注册和访问端点。
端点工作,特别是在登录端点中,会生成一个 JWT 令牌,然后将其保存在客户端的 localStorage 中。
const jwtStrategy = new JWTStrategy.Strategy(
{
jwtFromRequest: JWTStrategy.ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET,
},
async (jwtPayload, done) => {
try {
const user = await findUserById(jwtPayload.user.id);
if (user) {
return done(null, user);
} else {
return done(null, false);
}
} catch (error) {
console.error("Error finding user by ID:", error);
return done(error, false);
}
}
);
passport.use(jwtStrategy);
// Signup endpoint
app.post("/api/signup", async (req, res) => {
try {
const { name, username, email, password } = req.body;
// Verify if user already exists
const existingUser = await findUserByEmail(email);
if (existingUser) {
return res.status(400).json({ message: "Email already exists" });
}
// Password hashing
const hashedPassword = await bcrypt.hash(password, 10);
// Create new user
const newUser = await signUp(name, username, email, hashedPassword);
res.status(201).json({ message: "User created successfully" });
} catch (error) {
console.error("Error signing up:", error);
res.status(500).json({ message: "Internal server error" });
}
});
// Login endpoint
app.post("/api/login", async (req, res) => {
try {
const { email, password } = req.body;
// Verify if user already exists
const user = await findUserByEmail(email);
if (!user) {
return res.status(401).json({ message: "User not found" });
}
// Verify password match
const passwordMatch = await bcrypt.compare(password, user.password);
if (!passwordMatch) {
return res.status(401).json({ message: "Invalid email or password" });
}
// Token JWT creation
const token = jwt.sign({ user: user }, process.env.JWT_SECRET, {
expiresIn: "1h",
});
res.status(200).json({ token: token });
} catch (error) {
console.error("Error logging in:", error);
res.status(500).json({ message: "Internal server error" });
}
});
我还创建了一个 /api/validateToken 端点(使用passport.authenticate()),它除了返回成功消息和用户对象(如果令牌有效)之外什么也不做。
后端一切都很好,但我在前端渲染元素时遇到了一些问题。在我的标题组件中,如果用户未经过身份验证,我想显示两个按钮“登录”和“注册”,如果用户经过身份验证,则显示两个按钮“最喜欢的城市”和“注销”。
最初,我尝试了以下方法:
import React from "react";
import { Link } from "react-router-dom";
import "../styles/Header.css";
import { validateToken, logOut } from "../client-utils";
function Header() {
const token = localStorage.getItem("token");
const [loading, setLoading] = React.useState(true);
const [isTokenValid, setIsTokenValid] = React.useState(null);
React.useEffect(() => {
validateToken(token, setIsTokenValid, setLoading);
}, [token]);
function handleLogout() {
logOut(token, setIsTokenValid);
}
return (
<header>
{loading && (
<div className="loading-container">
<div className="loading-spinner"></div>
</div>
)}
<nav>
<ul>
{isTokenValid ? (
<>
<li>
<Link to="/">Preferred Cities</Link>
</li>
<li>
<button onClick={handleLogout}>Logout</button>
</li>
</>
) : (
<>
<li>
<Link to="/signup">Sign Up</Link>
</li>
<li>
<Link to="/login">Sign In</Link>
</li>
</>
)}
</ul>
</nav>
</header>
);
}
export default Header;
用 validateToken() 和 logOut() 声明如下:
async function validateToken(token, setIsTokenValid, setLoading) {
try {
const response = await axios.get(
"http://localhost:5000/api/validateToken",
{
headers: {
Authorization: `Bearer ${token}`,
},
}
);
console.log(response.data);
setIsTokenValid(true);
} catch (err) {
console.log("Errore nella validazione del token");
setIsTokenValid(false);
} finally {
setLoading(false);
}
}
async function logOut(token, setIsTokenValid) {
try {
const response = await axios.get("http://localhost:5000/api/logout", {
headers: {
Authorization: `Bearer ${token}`,
},
});
console.log(response.data);
localStorage.removeItem("token");
setIsTokenValid(false);
} catch (err) {
console.log(err);
}
}
注销逻辑正常工作,并且注销时元素可以正确呈现。
但在进行身份验证后,我被迫刷新页面以查看更新的元素(即首选城市和注销按钮)。
React.useEffect(() => {
async function checkToken() {
if (token) {
await validateToken(token, setIsTokenValid, setLoading);
} else {
setLoading(false);
}
}
checkToken();
}, [token]);
但不幸的是,它仍然需要刷新才能正确显示项目。有谁知道我该如何解决这个问题?我已经研究了4个多小时了,没有任何结果。
预先感谢任何可以帮助我的人。
useEffect
仅在组件渲染时执行。这里
token
中的值将发生变化,但该变化不会触发重新渲染,因为它不是组件状态或父状态的一部分。为了让
useEffect
注意到组件需要渲染的变化。
与事件不同,效果是由渲染本身而不是特定的交互引起的。请参阅
文档了解更多信息。还建议通读全部内容。对于了解用法和陷阱也很有帮助。
因此,您可以将响应存储在顶层的上下文(或某些状态管理库)中,以便组件树重新渲染并使用该值。