You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React登录页出现TypeError: Cannot read property 'token' of undefined问题求助

解决React Auth Context中的TypeError: Cannot read property 'token' of undefined错误

嘿,我来帮你一步步解决这个问题!咱们先定位错误根源,再逐个修复,顺便给你补点token和授权的基础知识点。

错误直接原因

你在Login.js里尝试读取response.token和response.data.token,这里有两个核心问题:

  1. Fetch返回的response对象本身没有token或data属性——你需要先把响应解析成JSON格式,而不是纯文本。
  2. 你用了response.text()读取响应,但后端返回的应该是包含token的JSON数据,不是纯字符串"redirect"。

步骤1:修复Login组件的API请求逻辑

先修改Login.js里的请求处理,同时解决函数组件不能用this.props.history的问题:

import React, { useState, useContext, useHistory } from "react";
import MuiThemeProvider from "material-ui/styles/MuiThemeProvider";
import AppBar from "material-ui/AppBar";
import RaisedButton from "material-ui/RaisedButton";
import TextField from "material-ui/TextField";
import { Link } from "react-router-dom";
import { AuthContext } from "./auth";
import "./App.css";

const Login = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const { setToken } = useContext(AuthContext);
  const history = useHistory(); // 用useHistory hook替代this.props.history

  const performLogin = async e => {
    e.preventDefault();
    const body = { password, email };
    console.log(body);
    
    const options = {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        Accept: "application/json"
      },
      body: JSON.stringify(body)
    };

    try {
      const response = await fetch("/api/authenticate", options);
      // 把response.text()改成response.json(),解析后端返回的JSON数据
      const data = await response.json();
      
      // 请和后端确认登录成功的返回格式,这里假设后端返回{ success: true, token: "xxx" }
      if (data.success) {
        setToken(data.token); // 将后端返回的token存入Context
        history.push("/dashboard"); // 登录成功后直接跳转
      } else if (data.message === "verifyemail") {
        history.push(`/verifyOtp/${email}`); // 直接用当前email变量
      } else {
        console.log("login failed");
        window.alert("login failed");
      }
    } catch (error) {
      console.error(error);
    }
  };

  return (
    <>
      <div className="loginForm">
        <MuiThemeProvider>
          <TextField
            hintText="Enter your Email"
            floatingLabelText="Email"
            onChange={(event, newValue) => setEmail(newValue)}
          />
          <br />
          <TextField
            type="password"
            hintText="Enter your password"
            floatingLabelText="password"
            onChange={(event, newValue) => setPassword(newValue)}
          />
          <br />
          <RaisedButton
            label="Submit"
            primary={true}
            style={{ margin: 15 }}
            onClick={performLogin}
          />
        </MuiThemeProvider>
      </div>
    </>
  );
};

export default Login;

步骤2:修复AuthContext的Reducer逻辑

你的auth.js里的reducer写法不符合useReducer规范(应为(state, action)格式),同时优化localStorage同步逻辑:

import React, { createContext, useReducer, useEffect } from "react";

// 重构reducer,用action类型区分操作
const reducer = (state, action) => {
  switch (action.type) {
    case "SET_TOKEN":
      if (!action.payload) {
        localStorage.removeItem("token");
        return null;
      }
      return action.payload;
    case "CLEAR_TOKEN":
      localStorage.removeItem("token");
      return null;
    default:
      return state;
  }
};

// 从localStorage初始化token
const initialState = localStorage.getItem("token") 
  ? JSON.parse(localStorage.getItem("token")) 
  : null;

const AuthContext = createContext();

function AuthProvider(props) {
  const [token, dispatch] = useReducer(reducer, initialState);

  // token变化时同步到localStorage
  useEffect(() => {
    if (token) {
      localStorage.setItem("token", JSON.stringify(token));
    } else {
      localStorage.removeItem("token");
    }
  }, [token]);

  // 给Context提供统一操作方法
  const setToken = (newToken) => {
    dispatch({ type: "SET_TOKEN", payload: newToken });
  };

  const logOut = () => {
    dispatch({ type: "CLEAR_TOKEN" });
  };

  return (
    <AuthContext.Provider value={{ token, setToken, logOut }}>
      {props.children}
    </AuthContext.Provider>
  );
}

export { AuthContext, AuthProvider };

步骤3:修复App.js的路由顺序

原Switch里Redirect在PrivateRoute前面,会导致Dashboard永远无法匹配,调整顺序:

import React from "react";
import { BrowserRouter as Router, Redirect, Switch, Route, Link } from "react-router-dom";
import "./App.css";
import Login from "./login";
import Dashboard from "./dashboard";
import { AuthProvider } from "./auth";
import PrivateRoute from "./PrivateRoute";

function App(props) {
  return (
    <>
      <AuthProvider>
        <Router>
          <div className="container">
            <nav className="navbar navbar-expand-lg navheader">
              <div className="collapse navbar-collapse">
                <ul className="navbar-nav mr-auto">
                  <li className="nav-item">
                    <Link to={"/Login"} className="nav-link">
                      Login
                    </Link>
                  </li>
                </ul>
              </div>
            </nav>
            <br />
            <Switch>
              <Route exact path="/login" component={Login} />
              {/* PrivateRoute放在Redirect前面 */}
              <PrivateRoute exact path="/dashboard" component={Dashboard} />
              {/* 默认路由重定向到登录页 */}
              <Route render={() => <Redirect to="/login" />} />
            </Switch>
          </div>
        </Router>
      </AuthProvider>
    </>
  );
}

export default App;

步骤4:优化Dashboard组件

直接使用AuthContext提供的logOut方法,简化代码:

import React, { useContext } from "react";
import { AuthContext } from "./auth";

const Dashboard = () => {
  const { logOut } = useContext(AuthContext);

  return (
    <>
      <div>hello</div>
      <button onClick={logOut}>Logout</button>
    </>
  );
};

export default Dashboard;

关于Token和授权的基础科普

  • Token是什么?:它是后端生成的一串加密字符串,用来证明你已成功登录。前端拿到后需存在localStorage/Context中,后续请求后端接口时,要把Token放在请求头(比如Authorization: Bearer ${token}),后端通过验证Token判断你是否有权限访问受保护资源。
  • 这次错误是前端还是后端问题?:主要是前端处理响应的方式不对,但你需要和后端确认:
    1. 登录接口返回的JSON格式(是否包含token字段)
    2. 登录成功/失败/需验证邮箱时的返回标识

内容的提问来源于stack exchange,提问作者UbuntuNewb

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:17:24