0

I am validating form from server side. once I get error message, I wants to show error message on respective textbox field's error message

client side Object

const formFields = {
    firstName: {
        helperText: '',
        error: false
    },
    lastName: {
        helperText: '',
        error: false
    },
    emailID: {
        helperText: '',
        error: false
    },
    phoneNo: {
        helperText: '',
        error: false
    },
    password: {
        helperText: '',
        error: false
    },
    confirmPassword: {
        helperText: '',
        error: false
    }
}

Server side response object after validation

const responseError = errorData.response.data.errors //below object is the response
//{
    //"LastName": ["Enter LastName"],
    //"FirstName": ["Enter FirstName"],
    //"ConfirmPassword": ["Enter Confirm Password","confirm password do not match"]
//}

useState

const [inpValues, setInpValues] = useState(formFields)

Conditions to update

if ClientSideObj.key === responseObj.key then setInpValues of error and helperText field

const responseError = errorData.response.data.errors
console.log(responseError)

var FormFieldName = ""
for (keys in formFields) {
    console.log('FormField keys = ' + keys)

    for (var errorKeys in responseError) {
        if (keys.toLowerCase() === errorKeys.toLowerCase()) {

            console.log('* MATCHED FIELDS = ' + errorKeys) 
            //Matched 3 fields(LastName,FirstName,ConfirmPassword) successfully

            FormFieldName = keys
            
                    
            setInpValues(prevInpValues => ({
                ...prevInpValues,
                [FormFieldName]: {
                    ...prevInpValues[FormFieldName],
                    error: true,
                    helperText: responseError[errorKeys]
                    }
                })
            )
            console.table(inpValues)
        }
    }
}

Note

I go through this stack overflow already, then I passed previousState values also. still result same.

It's updating only the last for loop condition value

If the response.error object has return only one field, then it's updating that one

Liam neesan
  • 2,282
  • 6
  • 33
  • 72

2 Answers2

1

What you should do in order to avoid unnecessary extra renders its to loop inside setState callback:

setInpValues(prevInpValues => {

    for (const formKey in formFields) {
     
      for (const errorKey in responseError) {
          if (formKey.toLowerCase() === errorKey.toLowerCase()) {

              prevInpValues[formKey] = {
                ...prevInpValues[formKey],
                error: true,
                helperText: responseError[errorKey]
              }
          }
      }
  }
return {...prevInpValues}
}
Miguel Hidalgo
  • 374
  • 2
  • 4
  • 1. you're mutating the current state; don't . Then you're returning a copy so that `setInpValues` triggers a rerender. Turn that around, make the copy first and then mutate and return that copy. 2. You have mismatching brackets and your `return` is inside the loop. – Thomas Oct 29 '22 at 21:39
1

It's updating only the last for loop condition value

as Miguel Hidalgo states, you should make all changes in one update:

const responseError = errorData.response.data.errors
console.log(responseError)

setInpValues(state => {
  const newState = { ...state };
  for (let errorKey in responseError) {
    for (let formField in formFields) {
      // this would be so much simpler if your properties would be in the correct case and you'd not have to do this dance with `.toLowerCase()`
      if (formField.toLowerCase() !== errorKey.toLowerCase()) {
        continue;
      }

      newState[formField] = {
        ...newState[formField],
        error: true,
        helperText
      }
    }
  }

  return newState
});

If errorKey and fieldName would be identical and you'd not have to match them case insensitive you could write this:

const responseError = errorData.response.data.errors
console.log(responseError)

setInpValues(state => {
  const newState = { ...state };
  for (let formField in responseError) {
    newState[formField] = {
      ...newState[formField],
      error: true,
      helperText: responseError[formField]
    }
  }
  return newState
});
Thomas
  • 11,958
  • 1
  • 14
  • 23
  • thanks for your time. both you and @miguel are working. but could you please tell me why do you make `newState` object and return that `newState` instead of directly update the `prevState` like @miguel mentioned. – Liam neesan Oct 30 '22 at 14:28
  • one more question to understand how the logic works. Why it didn't update when I setValues inside loop?. I setValues once the loop condition is satisfied, but it doesn't update. but you use the loop inside the setValue, that is only difference – Liam neesan Oct 30 '22 at 14:56
  • 1
    @Liamneesan I've linked you two related questions in this answer. The issue is a combination of `var`, loops and any kind of async code, wether it's Promises, callbacks, Events, or whatever. It's working inside the `setInpValues` because 1. I use `let` and `const` and 2. `var` would now be used synchronously. Please read up on the differences between `let`/`const` and `var`, `hoisting` and why in the past there were so many IIFEs inside of loops. – Thomas Oct 30 '22 at 16:02