上下文中的 React useState 不更新

React useState in context does not update

我正在使用 React 创建一个登录页面,登录后应该跟踪用户是否登录。我认为使用 React 上下文和状态挂钩比使用像 Redux 这样大而复杂的东西更容易.

我创建了一个有效的登录功能,在成功登录后,它应该会在我的上下文中更新我的状态。登录有效(我的状态为 200)但我的状态未在我的上下文中更新。

我的'AuthContext.jsx'看起来像这样

import React, { createContext, useState } from "react";
import { login, register } from "../API/apiMethods";

export const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [authenticated, setAuthenticated] = useState(false);

  const authSetter = (state) => {
    setAuthenticated(state);
  };

  const authGetter = () => {
    return authenticated;
  };

  return (
    <AuthContext.Provider
      value={{
        authSetter,
        authGetter,
        login,
        register,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};

我的登录函数是这样的

/**
 * @description Handles login
 * @param {String} email
 * @param {String} password
 * @returns {Boolean}
 */
export const login = async(email, password) => {
  try {
    let authenticated = false;
    await fetch(BASE_URL + "login", {
      method: "POST",
      credentials: "include",
      headers: {
        "Content-Type": "application/json"
      },
      body: JSON.stringify({
        email,
        password
      }),
    }).then((res) => {
      authenticated = res.status === 200 ? true : false;
    });
    return authenticated;
  } catch (err) {
    console.log(err);
    return false;
  }
};

在我的登录表单中,我尝试在成功登录后更新身份验证布尔值

const {
  login,
  authSetter,
  authGetter
} = useContext(AuthContext);
const submit = async(e) => {
  e.preventDefault();

  await login(email, password)
    .then((authSuccess) => {
      if (authSuccess) {
        console.log("login successfull");
        authSetter(true);
      }
    })
    .then(() => console.log(authGetter()));
};

使用此代码,我希望控制台输出为带有 'login successfull' 的打印字符串和打印的布尔值 true。 但似乎我的状态没有更新,即使我调用了 setter.

不知道为什么更新不了,谁能帮帮我?

这段代码完全符合预期。当您更新状态时,它不会立即发生。

const submit = async(e) => {
  e.preventDefault();

  await login(email, password)
    .then((authSuccess) => {
      if (authSuccess) {
        console.log("login successfull");
        authSetter(true);
      }
    })
    .then(() => console.log(authGetter()));
};

当您调用 authSetter(true); 时,状态更新会排队,一旦 then 回调完成,它就会转到链中的下一个 then,其中有您的 authGetter()。现在状态更新不会像我解释的那样立即发生,它是排队的。因此,当执行最后一个 then 回调时,排队的状态更新没有发生,您仍然看到 false 这是旧值。

您可以通过以下方式重构您的 AuthProvider,无需将 setter 包装在函数中,因为它会在更新状态时创建函数的新实例(另一方面,useState return 是 setter 函数的记忆值),您可以简单地 return authenticated 状态而无需 getter再次遇到同样的问题。

import React, { createContext, useState } from "react";
import { login, register } from "../API/apiMethods";

export const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [authenticated, setAuthenticated] = useState(false);

  return (
    <AuthContext.Provider
      value={{
        setAuthenticated,
        authenticated,
        login,
        register,
      }}
    >
      {children}
    </AuthContext.Provider>
  );
};

在您的表单中,您可以多一个useEffect来检查您是否已成功登录。当 authenticated 状态更新后,useEffect 将 运行。

const {
  login,
  setAuthenticated,
  authenticated
} = useContext(AuthContext);

const submit = async(e) => {
  e.preventDefault();
  const authSuccess = await login(email, password);
  if (authSuccess) {
     console.log("login successfull");
     authSetter(true);
  }
};

useEffect(() => {
  console.log(authenticated);
}, [authenticated]);