MVP模式下React Hooks登录模块错误传递与展示问题求助
在React MVP模式下实现Presenter向View传递错误信息并展示的正确方式
问题描述
我正在开发应用的登录视图,采用MVP(Model-View-Presenter)模式,想在Presenter中捕获错误,再将其作为props传递给View并在View中处理展示,但遇到了问题。
第一个方案(不符合MVP规范)
直接在Presenter中操作DOM处理错误,虽然能运行但违反MVP规则:
try {await props.model.signIn(email, password) } catch (error) { var errorMessage = error.message; if (errorMessage.includes("password")) { document.getElementById("error").innerHTML = "Wrong password, try again"}
第二个方案(遇到props.error未定义问题)
尝试用React State Hooks处理,但View中提示props.error未定义,相关代码如下:
Presenter代码(Login.js)
import LoginView from "../views/loginView"; import React from "react"; function Login(props) { const [error,setError] = React.useState('') async function handleLoginACB() { var email = document.getElementById('email').value var password = document.getElementById('password').value try { await props.model.signIn(email, password) } catch (error) { console.log(error.message) setError(error) } if (props.model.currentUser != null) { window.location.hash = "#HomeScreen" } } return <LoginView onUserLogIn={handleLoginACB} error = {error} /> } export default Login;
View代码(LoginView.js)
function LoginView(props) { return( <div className="loginBox"> <img src="logo.svg" className="image blob"/> <form> <input type = "email" placeholder = "Email" id="email"></input> <div><input type = "password" placeholder = "Password" id="password"></input></div> <div id = "error">{errorHandling}</div> <a href="#CreateAccount">Create a new account</a> </form> <button onClick={props.onUserLogIn}>Log in</button> </div>); function errorHandling(){ console.log(props.error.message) if (props.error.message.includes("password")) { return "Wrong password, try again" } if (props.error.message.includes("email")) { return "Wrong email, try again" } } } export default LoginView;
解决方案
1. 修复初始状态类型不匹配问题
Presenter中初始状态设为空字符串'',但后续赋值的是Error对象,导致初始时props.error是字符串,调用.message会报错。修改初始状态为null:
const [error, setError] = React.useState(null);
2. 调用errorHandling函数(而非引用函数)
View中渲染错误的地方,要调用函数而不是只写函数名,否则不会执行:
<div id="error">{errorHandling()}</div>
3. 添加错误存在的判断,避免空值报错
在errorHandling函数中先判断props.error是否存在,再访问.message:
function errorHandling(){ if (!props.error) return ''; if (props.error.message.includes("password")) { return "Wrong password, try again"; } if (props.error.message.includes("email")) { return "Wrong email, try again"; } return ''; }
4. 优化:Presenter不要直接操作DOM(符合MVP规范)
当前Presenter中通过document.getElementById获取输入值,这违反了MVP原则(Presenter不应直接操作View的DOM)。应该让View管理输入状态,将值传递给Presenter:
修改后的LoginView.js(管理输入状态)
import React, { useState } from "react"; function LoginView(props) { const [email, setEmail] = useState(''); const [password, setPassword] = useState(''); const handleLogin = () => { props.onUserLogIn(email, password); }; function errorHandling(){ if (!props.error) return ''; if (props.error.message.includes("password")) { return "Wrong password, try again"; } if (props.error.message.includes("email")) { return "Wrong email, try again"; } return ''; } return( <div className="loginBox"> <img src="logo.svg" className="image blob"/> <form> <input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} /> <div> <input type="password" placeholder="Password" value={password} onChange={(e) => setPassword(e.target.value)} /> </div> <div id="error">{errorHandling()}</div> <a href="#CreateAccount">Create a new account</a> </form> <button onClick={handleLogin}>Log in</button> </div>); } export default LoginView;
修改后的Login.js(Presenter接收参数)
import LoginView from "../views/loginView"; import React, { useState } from "react"; function Login(props) { const [error, setError] = useState(null); async function handleLoginACB(email, password) { try { await props.model.signIn(email, password); if (props.model.currentUser != null) { window.location.hash = "#HomeScreen"; } setError(null); // 登录成功后清空错误 } catch (error) { console.log(error.message); setError(error); } } return <LoginView onUserLogIn={handleLoginACB} error={error} />; } export default Login;
5. 额外优化:路由跳转用React Router(如果使用)
如果项目中使用React Router,建议用useNavigate代替window.location.hash,更符合React生态:
import { useNavigate } from 'react-router-dom'; function Login(props) { const navigate = useNavigate(); // ... async function handleLoginACB(email, password) { try { await props.model.signIn(email, password); if (props.model.currentUser != null) { navigate("/HomeScreen"); } // ... } // ... } }
内容的提问来源于stack exchange,提问作者overlythinking
相关产品推荐
相关产品推荐

