使用Material UI Input组件时登录流程无法执行 接口报500错误
问题描述
在登录页面点击Log In按钮时,浏览器控制台抛出错误:POST http://localhost:3000/auth/login 500 (Internal Server Error)
完整实现代码
import { Link } from "react-router-dom" import React from "./login.css" import "./login.css"; import { useContext, useRef } from "react"; import { Context } from "../../context/Context"; import axios from "axios"; import { useState } from "react" import Helmet from "react-helmet"; import VisibilityIcon from '@mui/icons-material/Visibility'; import IconButton from '@mui/material/IconButton'; import InputLabel from '@mui/material/InputLabel'; import InputAdornment from '@mui/material/InputAdornment'; import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import Input from '@mui/material/Input'; export default function Login() { const userRef = useRef(); const passwordRef = useRef(); const { dispatch, isFetching } = useContext(Context); const [passwordShown, setPasswordShown] = useState(false); const togglePassword = () => { // 触发时反转密码显示状态 setPasswordShown(passwordShown ? false : true); }; const handleSubmit = async (e) => { e.preventDefault(); dispatch({ type: "LOGIN_START" }); try { const res = await axios.post("/auth/login", { username: userRef.current.value, password: passwordRef.current.value, }); dispatch({ type: "LOGIN_SUCCESS", payload: res.data }); } catch (err) { dispatch({ type: "LOGIN_FAILURE" }); } }; return ( <div className="login"> <Helmet> <title>| Log In</title> <meta name="description" content="Sign In" /> </Helmet> <span className="loginTitle">Login</span> <form className="loginForm" onSubmit={handleSubmit}> <label>Email</label> <div class="input-icons"> <i class="fa-solid fa-envelope icon"></i> <input type="text" className="loginInput" placeholder="Enter your email..." ref={userRef} /> </div> <InputLabel> Password </InputLabel> <Input type={passwordShown ? "text" : "password"} ref={passwordRef} required className="loginInput" placeholder="•••••••••••" endAdornment={ <InputAdornment position="end"> <IconButton onClick={togglePassword} >{passwordShown ? <VisibilityIcon /> : <VisibilityOffIcon />} </IconButton> </InputAdornment> } /> <button className="loginButton" type="submit" disabled={isFetching}> Login </button> {/* 注册按钮放在表单内,后续出问题可以优先排查这里 */} <button className="loginRegisterButton"> <Link className="link" to="/register">Don't have an account yet? Sign Up</Link> </button> </form> </div> ) }
已尝试排查操作
- 将Material UI的Input相关组件替换为原生HTML input标签后,登录流程可正常运行无报错。
待解决疑问
- 当前代码存在什么问题?
- 该错误是否由Material UI导致?
- 注册页面同样使用了Material UI组件,但未出现同类错误,原因是什么?
问题原因与解决方案
这个500错误确实和Material UI的Input组件使用方式有关,核心问题是ref绑定错误导致提交的密码值为undefined,后端接收到不符合预期的参数才抛出500错误。
具体原因
- Material UI的
Input是封装后的自定义组件,直接把useRef创建的ref挂在组件标签上时,拿到的passwordRef.current是MUI Input组件的实例,不是底层的原生input DOM元素,自然读不到.value属性,实际提交给接口的password字段是undefined。 - 替换成原生input标签后,ref可以直接绑定到DOM元素,能正常读取输入值,所以流程恢复正常。
- 注册页没出问题,大概率是注册页没有用ref直接读Input的值,而是用受控组件的方式(通过
value+onChange绑定state存储输入值),所以不会出现取值异常的问题。
修复方法
两种方案任选其一即可:
方案1:使用MUI提供的inputRef属性绑定ref
MUI Input组件专门提供了inputRef属性用来绑定底层原生input的ref,把代码里的ref={passwordRef}改成inputRef={passwordRef}就可以正常读取值:
<Input type={passwordShown ? "text" : "password"} inputRef={passwordRef} // 替换为inputRef required className="loginInput" placeholder="•••••••••••" // 其余属性保持不变 />
方案2:改成受控组件写法
和注册页逻辑保持一致,用useState存储密码输入值,不通过ref读取:
// 新增state存储密码 const [password, setPassword] = useState(""); // Input上绑定value和onChange <Input type={passwordShown ? "text" : "password"} required className="loginInput" placeholder="•••••••••••" value={password} onChange={(e) => setPassword(e.target.value)} // 其余属性保持不变 /> // 提交时直接取password变量即可 const res = await axios.post("/auth/login", { username: userRef.current.value, password: password, });
额外提示:代码开头的
import React from "./login.css"是无效导入,CSS文件不需要赋值给变量导入,直接删掉该行即可,不会影响登录逻辑,但能避免不必要的编译警告。另外用户名输入框的label标注为Email,但提交字段名是username,建议对齐前后端字段约定,避免后续出现参数不匹配问题。
内容的提问来源于stack exchange,提问作者sultan.h
相关产品推荐
相关产品推荐

