Formik & yup form validation not working as expected with VirtualizedSelect

41,566

It looks like the field is expecting the string to be required based on your validationSchema.

The error helped point me in the right direction. Here's the docs for Yup .nullable(): https://github.com/jquense/yup#mixednullableisnullable-boolean--true-schema

Try adding .nullable() to the chain of validations.

validationSchema={object().shape({ category: string().required("Category is required.").nullable() })}

Hope this helps. enter image description here

Share:
41,566
Kamran
Author by

Kamran

Updated on July 09, 2022

Comments

  • Kamran
    Kamran almost 2 years

    I created a form with formik in order to have form validations. I have used the components Formik, Form, Field form formik and configured them:

        import { Formik, Form, Field } from "formik";
        import { object, string } from "yup";
        import isEmpty from "lodash/isEmpty";
        import FormikSelectInput from "../common/FormikSelectInput";
    
        class App extends Component {
          render() {
            const options = this.props.categories.map(c => {
              return { label: c.name, value: c.name };
            });
    
            return (
              <Formik
                validationSchema={object().shape({
                  category: string().required("Category is required.")
                })}
                initialValues={this.props.obj}
                onSubmit={(values, actions) => {
                  console.log(values);
                }}
                render={({ errors, dirty, isSubmitting, setFieldValue }) => (
                  <Form>
                    <Field
                      name="category"
                      label="Categories"
                      value={this.props.obj.category.name}
                      options={options}
                      component={FormikSelectInput}
                    />
                    <button
                      type="submit"
                      className="btn btn-default"
                      disabled={isSubmitting || !isEmpty(errors) || !dirty}
                    >
                      Save
                    </button>
                  </Form>
                )}
              />
            );
          }
        }
    
        //Prop Types validation
        App.propTypes = {
          obj: PropTypes.object.isRequired,
          categories: PropTypes.array.isRequired,
          actions: PropTypes.object.isRequired
        };
        const getElementByID = (items, id) => {
      let res = items.filter(l => l.id === id);
      return res.length ? res[0] : null; //since filter returns an array we need to check for res.length
    };
        //Redux connect
        const mapStateToProps = ({ objects, categories }, ownProps) => {
          let obj = {
            id: "",
            name: "",
            category: { id: "", name: "" }
          };
          return {
            obj: getElementByID(objects, ownProps.match.params.id) || obj,
            categories: categories
          };
        };
    
        export default connect(
          mapStateToProps,
          {...}
        )(App);
    

    And I have a custom component 'FormikSelectInput':

    import React, { Component } from "react";
    import classnames from "classnames";
    import VirtualizedSelect from "react-virtualized-select";
    import "react-select/dist/react-select.css";
    import "react-virtualized/styles.css";
    import "react-virtualized-select/styles.css";
    
    const InputFeedback = ({ children }) => (
      <span className="text-danger">{children}</span>
    );
    
    const Label = ({ error, children, ...props }) => {
      return <label {...props}>{children}</label>;
    };
    
    class FormikSelectInput extends Component {
      constructor(props) {
        super(props);
        this.state = { selectValue: this.props.value };
      }
    
      render() {
        const {
          field: { name, ...field }, // { name, value, onChange, onBlur }
          form: { touched, errors }, // also values, setXXXX, handleXXXX, dirty, isValid, status, etc.
          className,
          label,
          ...props
        } = this.props;
    
        const error = errors[name];
        const touch = touched[name];
        const classes = classnames(
          "form-group",
          {
            "animated shake error": !!error
          },
          className
        );
    
        console.log("props", props);
        return (
          <div className={classes}>
            <Label htmlFor={name} error={error}>
              {label}
            </Label>
    <VirtualizedSelect
              name={name}
              id={name}
              className="form-control"
              {...field}
              {...props}
              onChange={(selectValue) => this.setState(() => {
                this.props.form.setFieldValue('category',selectValue)
                return { selectValue } 
              })}
              value={this.state.selectValue}
            /> 
            {touch && error && <InputFeedback>{error}</InputFeedback>}
          </div>
        );
      }
    }
    
    export default FormikSelectInput;
    

    My component is working and I am able to select an option, but why formik together with 'yup' validation showing me an error when I empty the select field.

    When I clear my select field I get an ERROR - 'category must be a string type, but the final value was: null. If "null" is intended as an empty value be sure to mark the schema as .nullable()'

    enter image description here

    My code is based on the this example.