React Hook form validation does not work on a controlled component

Viewed 1300

I'm fairly new to react hook form. I have a custom component made on top of material ui's autocomplete. The problem is that react hook form is not validating the field whatsoever. Here's how react hook form is setup

type FormData = {
  eventTypes: string[];
};

const FORM_SCHEMA = yup
  .object()
  .shape({
    eventTypes: yup.array().of(yup.string()).required('Please select event type(s)'),
  })
  .required();

const formInitialValues: FormData = {
  eventTypes: [],
};

const {
    handleSubmit,
    formState: { errors },
    register,
    watch,
    control,
  } = useForm<FormData>({ resolver: yupResolver(FORM_SCHEMA), defaultValues: formInitialValues });

  return (
    <Controller
        name="eventTypes"
        control={control}
        render={({ field, fieldState: { error } }) => (
          <MultipleSelection
            {...field}
            items={['Avg. Door Time', 'Door Cycles', 'Mileage', 'Trips']}
            label="Event Type(s)"
            placeholder="Select event type(s)"
            required
            showAllTag
            freeSolo={false}
            error={!!error}
            helperText={!!error?.message ?? ''}
            color={error ? 'error' : 'default'}
          />
        )}
      />
  );

The onChange of the component works fine and on submitting the form gets the values. But the validation on the field that it shouldn't be an empty array and is required is not working.

Here's how multipleSelection looks like

    <Autocomplete
      ref={autoCompleteContainerRef}
      multiple
      disabled={disabled}
      value={value}
      classes={{
        root: classes.root,
        focused: classes.inFocus,
        popupIndicator: classes.icon,
        popper: classes.popper,
        paper: classes.paper,
      }}
      className={classes.multipleSelect}
      options={items}
      size={size}
      closeIcon={
        <Icon
          component={CloseIcon}
          fontSize="small"
          classes={{ root: classes.icon }}
        />
      }
      autoComplete
      onChange={(event, newValue) => {
        handleChange(newValue);
      }}
      getLimitTagsText={(more) => (
        <Chip
          classes={{ root: clsx(classes.chip, classes.more) }}
          label={
            <Typography noWrap variant="caption">
              {`+${more} more`}
            </Typography>
          }
        />
      )}
      renderTags={handleRenderTags}
      renderInput={(params) => (
        <TextField
          {...params}
          {...textFieldProps}
          placeholder={isEmpty(value) ? placeholder : ''}
          className={classes.textField}
          onKeyDown={onKeyDown}
        />
      )}
      renderOption={(option, state) => (
        <StyledListItem
          label={optionLabel(option)}
          selected={state.selected}
          classes={{ label: classes.label }}
        />
      )}
      {...rest}
    />

Does anyone have an idea why the validation is not working. Any help would be appreciated.

1 Answers

As it turns out, the culprit was this piece of code

const FORM_SCHEMA = yup
  .object()
  .shape({
    eventTypes: yup.array().of(yup.string()).required('Please select event type(s)'), // <---
  })
  .required();

I changed it to the following

const FORM_SCHEMA = yup
  .object()
  .shape({
    eventTypes: yup
      .array()
      .of(yup.string())
      .test('empty-check', 'Please select event type(s)', value => value?.length !== 0),
  })
  .required();

And the validation works now.

I'm still not sure why it was not working in the first place. If anybody has a reasoning to why yup.array().of(yup.string()).required this was failing. Please let me know.

Related