I am a beginner in React ecosystem and I am having a go with forms in React, building a login form with two fields - email and password.
I am maintaining my entire form state in redux . For e.g :
const defaultState = {
touched: { email: false, password: false },
loginFormErrors: {email: '', password: ''},
emailValid: false,
passwordValid: false,
formValid: false
}
Some of these state values get updated by validateLoginFields
(reducer) which gets invoked when VALIDATE_LOGIN_FIELDS action is dispatched
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
}
I have few questions here :
Am I doing the right thing by maintaining client side validation details of my form (e.g valid, touched, formErrors etc) in Redux , or I am better of using local state for this purpose? Since I also store auth related data in redux, I just felt a little more comfortable by having Redux as my single source of truth for everything , but still want to validate this idea with rest of the community.
Whether is it ok for a reducer function like
validateLoginFields
to perform client side validations like I have shown in my example ? It does returns a new state - which is what a reducer does but this function is also performing validations for me... I feel something is wrong here and can be done in a better way ..I mean a more generalised validation? Is reducer the right place for validation like these?
So far I am struggling with client side validations and can't seem to figure out how should I structure my validator in a generic way so that I can scale it when my application grows big. Will be really thankful to community for any ideas on this.
I did try using Redux-form and React-redux-form , these libraries are great but just want to perform these validations on my own for now for learning purpose.
UPDATE :
I changed my actionCreator to look like this instead of doing all the validations in reducer. Still I can't seem to figure out where and how should I have my validations in shared way so that other email and password fields in my application can access this logic, not just the login component.
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 }
}
With the above change, I have put myself in an another issue where I am returning emailValid=false
, even if I am validating password field. If in my previous state emailValid=true
then it becomes false. This is happening because I override my state values. Is there any way I can access the current state in actionCreator so that I can return the same state values if nothing has changed?
But, as per this post accessing the state in actionCreator is not the best way..