我是 React 生态系统的初学者,我正在尝试使用 React 中的表单,构建一个包含两个字段(电子邮件和密码)的登录表单。
我正在 redux 中维护整个表单状态。例如:
const defaultState = {
touched: { email: false, password: false },
loginFormErrors: {email: '', password: ''},
emailValid: false,
passwordValid: false,
formValid: false
}
其中一些状态值由 validateLoginFields
(reducer)更新,当调度 VALIDATE_LOGIN_FIELDS 操作时会调用它
const validateLoginFields = (state, action) => {
const newLoginFormErrors = {}
let emailValid = state.emailValid
let passwordValid = state.passwordValid
let formValid = state.formValid
switch(action.key) {
case 'email':
if(!action.value){
emailValid = false
newLoginFormErrors.email = ''
}else{
const pattern = new RegExp("[^\\s@]+@[^@\\s]+\\.[^@\\s]+")
emailValid = pattern.test(action.value)
newLoginFormErrors.email = emailValid ? '' : 'Invalid email'
}
break
case 'password':
if(!action.value){
passwordValid = false
newLoginFormErrors.password = ''
}else{
passwordValid = action.value.length >= 6
newLoginFormErrors.password = passwordValid ? '': 'Password is too short!';
}
break
default:
break
}
formValid = emailValid && passwordValid
const loginFormErrorData = {}
Object.assign(loginFormErrorData, state.loginFormErrors, newLoginFormErrors)
const newState = {}
Object.assign(newState, state, { loginFormErrors: loginFormErrorData, emailValid: emailValid, passwordValid: passwordValid, formValid: formValid })
return newState
}
我有几个问题:
我在 Redux 中维护表单的客户端验证详细信息(例如 valid、touched、formErrors 等)是否正确,或者我最好使用本地状态来实现此目的?由于我也在 redux 中存储与身份验证相关的数据,因此将 Redux 作为所有事情的唯一事实来源让我感觉更舒服一些,但仍然想与社区其他人一起验证这个想法。
像
validateLoginFields
这样的reducer函数是否可以像我在示例中所示的那样执行客户端验证?它确实返回一个新状态 - 这是 reducer 所做的,但这个函数也在为我执行验证...我觉得这里出了问题,可以用更好的方式完成..我的意思是更通用的验证? reducer 是进行此类验证的正确场所吗?
到目前为止,我正在努力处理客户端验证,并且似乎无法弄清楚应该如何以通用方式构建我的验证器,以便在我的应用程序变大时可以扩展它。将非常感谢社区对此的任何想法。
我确实尝试过使用 Redux-form 和 React-redux-form ,这些库很棒,但现在只想自己执行这些验证以用于学习目的。
更新:
我将我的actionCreator更改为如下所示,而不是在reducer中进行所有验证。我仍然无法弄清楚应该在哪里以及如何以共享方式进行验证,以便应用程序中的其他电子邮件和密码字段可以访问此逻辑,而不仅仅是登录组件。
export function validateLoginFields(key, value) {
const newLoginFormErrors = {}
let emailValid = false
let passwordValid = false
let formValid = false
switch(key) {
case 'email':
if(!value){
emailValid = false
newLoginFormErrors.email = ''
}else{
const pattern = new RegExp("[^\\s@]+@[^@\\s]+\\.[^@\\s]+")
emailValid = pattern.test(value)
newLoginFormErrors.email = emailValid ? '' : 'Invalid email'
}
break
case 'password':
if(!value){
passwordValid = false
newLoginFormErrors.password = ''
}else{
passwordValid = value.length >= 6
newLoginFormErrors.password = passwordValid ? '': 'Password is too short!';
}
break
default:
break
}
formValid = emailValid && passwordValid
return { type: VALIDATE_LOGIN_FIELDS, newLoginFormErrors, emailValid, passwordValid, formValid }
}
通过上述更改,我将自己置于另一个问题中,即使我正在验证密码字段,我也会返回emailValid=false
。如果在我之前的状态 emailValid=true
那么它就会变成 false。发生这种情况是因为我覆盖了我的状态值。有什么方法可以访问 actionCreator 中的当前状态,以便在没有任何更改的情况下可以返回相同的状态值?
但是,根据这篇文章,访问 actionCreator 中的状态并不是最好的方法..
最佳答案
我将验证放入我的 reducer 中。 reducer 采用先前的状态和操作,然后返回一个新状态。
这是一个简单商店的结构:
{customer: {
firstName: "FatBoy",
lastName: "Slim",
email: 'foo@bar.com',
validationErrors: {}
}}
这是一个用于更新客户并验证电子邮件地址的 reducer :
function customerReducer(state = {}, action) {
switch (action.type) {
case 'UPDATE_CUSTOMER':
let validationErrors = {}
let emailError = validateEmail(action.customer.email);
if (emailError) {
validationErrors['email'] = emailError;
}
return {...state, ...action.customer, validationErrors};
default:
return state;
}
}
(注意。这是伪代码。从未被任何人运行过。您可以通过它来了解要点。)
对我来说,reducer 是放置验证的合理位置。我认为 Action 创建者的工作是发送更新存储所需的最小状态,而reducer的工作是将该状态转换为最佳应用程序所需的状态。我是那种认为验证是最佳的人,所以我在我的 reducer 中这样做。
关于reactjs - React Redux 中的表单状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44502193/