React 获取 localStorage 初始化值失败

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

大家好,我正在开发一个 Mern 应用程序,并且很好奇如何实现 jwt 令牌身份验证,它工作正常,但我前面遇到了一个小问题,用户登录后令牌在本地存储上成功设置,但是当我尝试在标头中发送令牌时使用 axios 获取值,第一次渲染时令牌值始终为空,我必须刷新页面,以便标头可以从本地存储访问令牌 这是useLogin组件,令牌立即设置在localStorage上

const login = async (email, password) => {
const user = { email, password };
setError(null);
try {
  const response = await axios.post(
    "http://localhost:4000/api/user/login",
    user
  );
  const result = response.data;

  console.log(response);

  if (response.statusText === "OK") {
    setError(null);
    setIsLoading(false);
    // save the token in local storage
    localStorage.setItem("user", JSON.stringify(result));
    // update the dispatch
    dispatch({ type: "LOGIN", payload: result });
  }
} catch (error) {
  const response = error.response.data;
  setError(response.message);

  //console.log(error);
}

};

这是我的 authContext,我试图在其中获取 localStorage 值


export const AuthContext = createContext();

export const authReducer = (state, action) => {
  switch (action.type) {
    case "LOGIN":
      return { user: action.payload.user };
    case "LOGOUT":
      return { user: null };

    default:
      return state;
  }
};

export const AuthContextProvider = ({ children }) => {
  const initialState = JSON.parse(localStorage.getItem("user")) || null;

  const [state, dispatch] = useReducer(authReducer, { user: initialState });
  // useEffect(() => {
  //   //check if there is a token in local storage
  //   const user = JSON.parse(localStorage.getItem("user"));
  //   console.log(user);

  //   if (user !== undefined && user !== null) {
  //     dispatch({ type: "LOGIN", payload: { user } });
  //   }
  // }, []);

  console.log("authContext state", state);

  return (
    <AuthContext.Provider value={{ ...state, dispatch }}>
      {children}
    </AuthContext.Provider>
  );
};

我尝试使用 useEffect ,它导致了同样的问题, getItem("user") 在第一次渲染时始终为空,并且在我刷新页面后工作正常

最后是我尝试获取数据的主组件

user.token 变量在第一次渲染时始终未定义,但在刷新时工作正常

import axios from "axios";
import { useWorkoutContext } from "../hooks/useWorkoutContext";
import { useAuthContext } from "../hooks/useAuthContext";
// component
import WorkoutDetails from "../components/workoutDetails";
import WorkoutForm from "../components/workoutForm";

const Home = () => {
  // const [workouts, setWorkouts] = useState([]);
  const { workouts, dispatch } = useWorkoutContext();
  const [error, setError] = useState(null);
  const { user } = useAuthContext();

  useEffect(() => {
    const fetchWorkouts = async () => {
      console.log(user.token);
      try {
        if (user && user.token) {
          const response = await axios.get(
            "http://localhost:4000/api/workouts",
            {
              headers: {
                Authorization: `Bearer ${user.token}`,
              },
            }
          );
          const result = response.data;
          if (response.status === 200) {
            //  console.log(result);
            // setWorkouts(result);
            dispatch({ type: "SET_WORKOUTS", payload: result });
          }
        }
      } catch (error) {
        console.error("Error fetching workouts:", error);
        setError("Error fetching workouts. Please check your network.");
      }
    };
    //console.log(user);
    if (user) {
      fetchWorkouts();
    }
  }, [dispatch, user]);
  return (
    <div className="home">
      <div className="workouts">
        {workouts &&
          workouts.map((workout) => {
            return <WorkoutDetails key={workout._id} workout={workout} />;
          })}
        {error && <p>{error}</p>}
      </div>
      <WorkoutForm />
    </div>
  );
};

export default Home;


这是我的返回 requireAuth 快速代码

import { userModel } from "../models/userModel.js";
const requireAuth = async (req, res, next) => {
  // verify authentication
  // we get authorization from the headers
  const { authorization } = req.headers;

  console.log(authorization);

  if (!authorization) {
    res.status(401).json({ error: "authorization required" });
  }
  // authorization is a string that contains the token
  let token;
  if (authorization) {
    token = authorization.split(" ")[1];
    console.log("Token is " + token);
  }

  try {
    // returns the id of the user that is in the token
    const { _id } = jwt.verify(token, process.env.SECRET);
    console.log("id is " + _id);
    req.user = await userModel.findOne({ _id }).select("_id");
    next();
  } catch (error) {
    console.log(error);
    res.status(401).json({ error: "request is not authorized" });
  }
};
export default requireAuth;

我已经坚持了两天了:'((

reactjs express local-storage token express-jwt
1个回答
0
投票

发生这种情况是因为您的响应由

email
token
user
对象组成。您将整个响应存储在本地存储中,因此您存储了一个由
email
token
user
组成的对象。但是,当您处理
LOGIN
事件时,您只存储响应中的
user
对象和其他两个键(
email
token
被丢弃)。

在分派 LOGIN 事件后的第一次渲染中,您尝试访问

token
键,但它不在用户对象中 - 它在用户对象之外,并且您尚未将其保存在减速器中。

刷新页面时,您会解析本地存储中的内容,在初始状态下,您将获得完整的响应,包括

email
token
user
对象。当您使用解析后的对象并请求
token
值时,它现在可以访问。

您需要进行以下更改:

export const authReducer = (state, action) => {
  switch (action.type) {
    case "LOGIN":
      return { user: action.payload}; // save the whole payload instead of just the user object (action.payload.user)
    case "LOGOUT":
      return { user: null };

    default:
      return state;
  }
};
© www.soinside.com 2019 - 2024. All rights reserved.