neoform icon indicating copy to clipboard operation
neoform copied to clipboard

Validation is out of sync with input

Open iurii-kyrylenko opened this issue 6 years ago • 2 comments

It seems that validation and input aren't synchronized

See also this gist

Proposed solution setTimeout(() => validate(), 0): perform validation at the next turn of event loop. It would be nice to solve the out of sync issue inside the neoform package.

iurii-kyrylenko avatar Dec 13 '17 00:12 iurii-kyrylenko

It's hard to know from inside of NeoForm should we postpone validation till the next tick or not, what do you think about something like onChange(e.target.value, validate) as a special case workaround? The value flow is async because input is controlled, i.e. we have an onChange ↔︎ value loop using state.

Another option is to always and explicitly pass values like validate(value) in most cases and validate(e.target.value) in case of onChange.

deepsweet avatar Dec 13 '17 08:12 deepsweet

@deepsweet Thank you for the analysis. You are right, the next tick looks like a hack. In our opinion, the second option (to pass e.target.value in case of onChange) is more preferable. More over, the pending pull request #13 allows easily detect the event type change. Below is a possible implementation of this idea, based on #13:

  1. Module fieldValidation.js. In method validate, along with name, pass an extra property value: event.target.value into form validation handler:
validate(event) {
  if (this.props.validator) {
    const type = event ? event.type : 'unknown';
    const value = (event && event.target) ? event.target.value : null;
    this.context.neoform.validate({ name: this.props.name, value }, type);
  }
}
  1. Module formValidation.js. In method validateField perfom validation, using passed event's value only in case of the change type. Otherwise, validate a value from the state:
validateField({ name, value: eventValue }, type) {
  const validator = this.validators[name];
  const value = (type === 'change') ? eventValue : this.context.neoform.getValue(name);
  validator(value, type)
    .then((message) => {
// ...

This is an example of the input component, with validation of state value on blur and event value on change:

const MyInput = ({
  value='',
  onChange,
  validate,
  validationStatus,
  validationMessage,
  ...props
}) => (
  <span>
    <input
      {...props}
      value={value}
      onBlur={validate}
      onChange={e => {
        onChange(e.target.value)
        if (validationStatus === false) {
          validate(e)
        }
      }}
    />
    {renderError(validationStatus, validationMessage)}
  </span>
)

iurii-kyrylenko avatar Dec 13 '17 17:12 iurii-kyrylenko