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

React项目登录按钮点击无响应问题求助(对接MongoDB)

React登录按钮无反应问题排查与修复

问题描述

我是React新手,已为对接MongoDB的React Web应用搭建认证系统,注册功能可正常使用,但使用已注册的用户信息点击登录按钮时无任何反应。相关代码如下:

RequireAuth.jsx

import { Navigate } from "react-router-dom";
import { useAuth } from './auth';

function getToken() {
    return localStorage.getItem('token');
}

function getInternalData() {
    return JSON.parse(localStorage.getItem('internalData'));
}

export const RequireAuth = ({ children }) => {
    const auth = useAuth();

    if (!getToken()) {
        return <Navigate to="/login" />;
    }
    return children;
};

export const RequireLogOut = ({ children }) => {
    const auth = useAuth();
    if (getToken()) {
        return <Navigate to="/home" />;
    }
    return children;
};

export { getToken, getInternalData };

auth.jsx

import {useState, useContext, createContext } from "react";

const AuthContext = createContext(null);

export const AuthProvider = ({children }) => {
    const [token, setToken] = useState("");

    const login = (token, internalData) => {
        console.log(token);
        localStorage.setItem('token', JSON.stringify(token));
        localStorage.setItem('internalData', JSON.stringify(internalData));
        setToken(token);
    };

    const logout = () => {
        localStorage.removeItem('token');
        localStorage.removeItem('internalData');
        setToken("");
    }

    return <AuthContext.Provider value={{token, login, logout}}>{children}</AuthContext.Provider>
};

export const useAuth = () => {
    return useContext(AuthContext);
};

login.jsx

import { useState } from 'react';
import { useNavigate, Link } from 'react-router-dom'
import { useAuth } from './auth';
import axios from 'axios'
import { LockClosedIcon } from '@heroicons/react/24/solid'
import { RequireAuth, RequireLogOut } from "./RequireAuth";


async function loginUser(credentials) {
    return fetch('http://localhost:4000/login', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json'
        },
        body: JSON.stringify(credentials)
    })
        .then(data => data.json())
}

export default function LoginForm() {

    const [loginValues, setLoginValues] = useState({ email: "", password: "" });
    const navigate = useNavigate()
    const [error, setError] = useState("");
    const auth = useAuth();


    const handleChange = (e) => {
        const { name, value } = e.target;
        const newValues = { ...loginValues, [name]: value };
        console.log(newValues);
        setLoginValues(newValues);

    };


    const handleSubmit = async e => {
        e.preventDefault();
        //const token = await loginUser(loginValues);
        const tokenData = await axios.post('http://localhost:4000/login/', loginValues);
        console.log(tokenData.data)
        if (tokenData.data.success) {
            setError("");
            auth.login(tokenData.data.token, tokenData.data.internalData);
        }
        else {
            setError("Credentials are incorrect. Try again");
        }
    }

    return (
        <>
            <div className="min-h-full flex items-center justify-center py-12 px-4 sm:px-6 lg:px-8">
                <div className="max-w-md w-full space-y-8">
                    <div>
                        <img
                            className="mx-auto h-36 w-auto"
                            src="/images/logo.png"
                            alt="Workflow"
                        />
                        <h2 className="mt-6 text-center text-3xl tracking-tight font-bold text-gray-900">
                            Sign in to your account
                        </h2>
                        <p className="mt-2 text-center text-sm text-gray-600">
                            Or{' '}
                            <a href="http://localhost:3000/register" className="font-medium text-gameblue hover:text-gamebluehover">
                                don't have an account yet?
                            </a>
                        </p>
                    </div>
                    {error ? <div>{error}</div> : ""}
                    <form onSubmit={handleSubmit} className="mt-8 space-y-6" action="#" method="POST">
                        <input type="hidden" name="remember" defaultValue="true" />
                        <div className="rounded-md shadow-sm -space-y-px">
                            <div>
                                <label htmlFor="email-address" className="sr-only">
                                    Email address
                                </label>
                                <input
                                    onChange={handleChange}
                                    value={loginValues.email}
                                    id="email-address"
                                    name="email"
                                    type="email"
                                    autoComplete="email"
                                    required
                                    className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-t-md focus:outline-none focus:ring-indigo-500 focus:border-gameblue focus:z-10 sm:text-sm"
                                    placeholder="Email address"
                                />
                            </div>
                            <div>
                                <label htmlFor="password" className="sr-only">
                                    Password
                                </label>
                                <input
                                    onChange={handleChange}
                                    value={loginValues.password}
                                    id="password"
                                    name="password"
                                    type="password"
                                    autoComplete="current-password"
                                    required
                                    className="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-gameblue focus:z-10 sm:text-sm"
                                    placeholder="Password"
                                />
                            </div>
                        </div>

                        <div className="flex items-center justify-between">
                            <div className="flex items-center">
                                <input
                                    id="remember-me"
                                    name="remember-me"
                                    type="checkbox"
                                    className="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded"
                                />
                                <label htmlFor="remember-me" className="ml-2 block text-sm text-gray-900">
                                    Remember me
                                </label>
                            </div>

                            <div className="text-sm">
                                <a href="#" className="font-medium text-gameblue hover:text-gamebluehover">
                                    Forgot your password?
                                </a>
                            </div>
                        </div>

                        <div>
                            <button
                                type="submit"
                                className="group relative w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white bg-gameblue hover:bg-gamebluehover focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500"
                            >
                                <span className="absolute left-0 inset-y-0 flex items-center pl-3">
                                    <LockClosedIcon className="h-5 w-5 text-gamebluelight group-hover:text-gamebluelightest" aria-hidden="true" />
                                </span>
                                Sign in
                            </button>
                        </div>
                    </form>
                </div>
            </div>
        </>
    );
}

问题排查与修复

1. 未处理Axios请求错误

handleSubmit函数中使用await axios.post但未捕获异常,若后端接口无响应、出现跨域错误或其他网络问题,会导致函数中断执行,按钮点击后无任何反馈。

修复代码:

const handleSubmit = async e => {
    e.preventDefault();
    try {
        const tokenData = await axios.post('http://localhost:4000/login/', loginValues);
        console.log(tokenData.data)
        if (tokenData.data.success) {
            setError("");
            auth.login(tokenData.data.token, tokenData.data.internalData);
            navigate('/home'); // 新增页面跳转逻辑
        } else {
            setError("Credentials are incorrect. Try again");
        }
    } catch (err) {
        console.error('登录请求失败:', err);
        setError('登录失败,请检查网络或后端服务状态');
    }
}

2. 登录成功后未跳转页面

登录成功调用auth.login后,没有触发页面跳转,用户看不到状态变化,误以为登录无反应。

修复:在auth.login后添加navigate('/home')(已包含在上述修复代码中)。

3. Token存储冗余JSON.stringify

auth.jsx中login函数对token执行JSON.stringify,若后端返回的token本身是字符串,会导致localStorage中存储的是带引号的字符串,后续getToken获取的值可能不符合预期,影响认证逻辑。

修复代码:

const login = (token, internalData) => {
    console.log(token);
    localStorage.setItem('token', token); // 移除冗余的JSON.stringify
    localStorage.setItem('internalData', JSON.stringify(internalData));
    setToken(token);
};

4. RequireAuth依赖localStorage而非AuthContext

RequireAuth组件直接从localStorage获取token,而非使用AuthContext中的状态,可能导致状态不同步(比如登录后AuthContext的token更新,但组件未重新渲染)。

修复代码:

export const RequireAuth = ({ children }) => {
    const { token } = useAuth();

    if (!token) {
        return <Navigate to="/login" />;
    }
    return children;
};

export const RequireLogOut = ({ children }) => {
    const { token } = useAuth();
    if (token) {
        return <Navigate to="/home" />;
    }
    return children;
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 01:10:36