useEffect() 钩子中的异步函数

问题描述 投票:0回答:1

我尝试使用 Express 服务器和 PostgreSQL 数据库在 React 中开发一个 Web 应用程序,同时也使用 OpenWeatherMap API。

我在服务器上配置了基于 JWT 令牌的身份验证策略,并创建了各种注册和访问端点。

端点工作,特别是在登录端点中,会生成一个 JWT 令牌,然后将其保存在客户端的 localStorage 中。

  • index.js(后端)
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()),它除了返回成功消息和用户对象(如果令牌有效)之外什么也不做。

后端一切都很好,但我在前端渲染元素时遇到了一些问题。

在我的标题组件中,如果用户未经过身份验证,我想显示两个按钮“登录”和“注册”,如果用户经过身份验证,则显示两个按钮“最喜欢的城市”和“注销”。

最初,我尝试了以下方法:

    标题.jsx
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); } }
注销逻辑正常工作,并且注销时元素可以正确呈现。

但在进行身份验证后,我被迫刷新页面以查看更新的元素(即首选城市和注销按钮)。

所以我在 useEffect 挂钩中尝试了其他方法:

React.useEffect(() => { async function checkToken() { if (token) { await validateToken(token, setIsTokenValid, setLoading); } else { setLoading(false); } } checkToken(); }, [token]);
但不幸的是,它仍然需要刷新才能正确显示项目。

有谁知道我该如何解决这个问题?我已经研究了4个多小时了,没有任何结果。

预先感谢任何可以帮助我的人。

reactjs express authentication jwt
1个回答
0
投票
仅将变量放入依赖数组中是不够的。


useEffect

仅在组件渲染时执行。这里 
token
 中的值将发生变化,但该变化不会触发重新渲染,因为它不是组件状态或父状态的一部分。
为了让
useEffect
 注意到组件需要渲染的变化。

与事件不同,效果是由渲染本身而不是特定的交互引起的。

请参阅

文档了解更多信息。还建议通读全部内容。对于了解用法和陷阱也很有帮助。

因此,您可以将响应存储在顶层的上下文(或某些状态管理库)中,以便组件树重新渲染并使用该值。

© www.soinside.com 2019 - 2024. All rights reserved.