4

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 :

  1. 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.

  2. 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..

Accessing Redux state in an action creator?

FE_Addict
  • 647
  • 2
  • 11
  • 21
  • I would put the validation in the actionCreator(). Usually, reducers just take a new state as a parameter and do nothing more – Titouan56 Jun 12 '17 at 14:42

1 Answers1

3

I put my validation in my reducers. Reducers take the previous state and an action and then return a new state.

Here's the structure of a simple store:

{customer: {
  firstName: "FatBoy",
  lastName: "Slim",
  email: 'foo@bar.com',
  validationErrors: {}
}}

Here's a reducer that updates the customer and does validation of the email address:

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;
   }
}

(Note. This is pseudo-code. It has never been run by anyone ever. It's there for you to get the gist.)

To me, the reducer is the logical place to put your validation. They way I think of it is that the action creator's job is to send the minimum state necessary to update the store, while the reducer's job is to transform that state into the optimal state needed by the application. And I'm the sort of guy who thinks that validation is optimal, so I do that in my reducers.

Gregory Higley
  • 15,923
  • 9
  • 67
  • 96
  • This is pretty cool. The redux-form alternative is much more complicated. – bowsersenior Mar 30 '18 at 01:54
  • 1
    This looks not ideal to me.Validation will happen after any calls to server: we put AJAX calls in ActionCreators (async) and they stand before the Reducer in the Redux Flow - and usually validation should be done before any AJAX requests. – Ashot Dec 20 '20 at 14:05