Formik: <Field /> and undefined initial value fires error

Created on 23 Dec 2017  路  8Comments  路  Source: formium/formik

if value in <Formic initialVales> is undefined React throws error

Warning: A component is changing an uncontrolled input of type text to be controlled. Input elements should not switch from uncontrolled to controlled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://fb.me/react-controlled-components
in input (created by Field)

const InnerForm2=()=>(
    <Form className={styles.Form}>
            <Field type={"text"} name={"clusterName"}/>
    </Form>
)
...
             <Formik
                    validate={this.validate as any}
                    validateOnBlur
                    onSubmit={values => console.log(values)}
                    initialValues={{clusterName: undefined,type:1}}
                    component={InnerForm2 as any}
                />
...

with undefined value <input /> rendered _without_ value attribute and first update causes value rendering and this throw error

Most helpful comment

@jaredpalmer This is fine for fields of type string (I'm using Typescript), you can initialize as an empty string. But for inputs of type number, you have to use an initial value of null or undefined if you don't want a value to appear in the input by default. And this continues to show the "uncontrolled input" warning.

Here is my code:

export interface InvoiceLevelTotal {
  amount?: number;
  currency?: string;
}

export enum FormKeys {
  Amount = 'amount',
  Currency = 'currency'
}

const formSchema: Yup.ObjectSchema<InvoiceLevelTotal> = Yup.object().shape({
  amount: Yup.number().required('Amount is required'),
  currency: Yup.string().required('Currency is required')
});

const initialFormValues: InvoiceLevelTotal = {
  amount: undefined,
  currency: ''
};

<Formik
  initialValues={initialFormValues}
  onSubmit={(values, { resetForm }) => {
    this.addInvoiceLevelTotal(values);
    resetForm();
  }}
  validationSchema={formSchema}>
    {(formProps: FormikProps<InvoiceLevelTotal>) => (
      <Form>
        <Field
          name={FormKeys.Amount}
          render={({ field, form }: FieldProps<InvoiceLevelTotal>) => (
            <input
              className="input-block-level"
              maxLength="20"
              {...field}
            />
          )}
        />
        <Field
          name={FormKeys.Currency}
          render={({ field, form }: FieldProps<InvoiceLevelTotal>) => (
            <input
              className="input-block-level"
              autoComplete="off"
              {...field}
            />
          )}
        />
      </Form>
    )}
</Formik>

All 8 comments

Correct. You must initialize values just as you would with React state

@jaredpalmer This is fine for fields of type string (I'm using Typescript), you can initialize as an empty string. But for inputs of type number, you have to use an initial value of null or undefined if you don't want a value to appear in the input by default. And this continues to show the "uncontrolled input" warning.

Here is my code:

export interface InvoiceLevelTotal {
  amount?: number;
  currency?: string;
}

export enum FormKeys {
  Amount = 'amount',
  Currency = 'currency'
}

const formSchema: Yup.ObjectSchema<InvoiceLevelTotal> = Yup.object().shape({
  amount: Yup.number().required('Amount is required'),
  currency: Yup.string().required('Currency is required')
});

const initialFormValues: InvoiceLevelTotal = {
  amount: undefined,
  currency: ''
};

<Formik
  initialValues={initialFormValues}
  onSubmit={(values, { resetForm }) => {
    this.addInvoiceLevelTotal(values);
    resetForm();
  }}
  validationSchema={formSchema}>
    {(formProps: FormikProps<InvoiceLevelTotal>) => (
      <Form>
        <Field
          name={FormKeys.Amount}
          render={({ field, form }: FieldProps<InvoiceLevelTotal>) => (
            <input
              className="input-block-level"
              maxLength="20"
              {...field}
            />
          )}
        />
        <Field
          name={FormKeys.Currency}
          render={({ field, form }: FieldProps<InvoiceLevelTotal>) => (
            <input
              className="input-block-level"
              autoComplete="off"
              {...field}
            />
          )}
        />
      </Form>
    )}
</Formik>

Maybe you can try to write:
amount?: number | string;

and in the _initialFormValues_:
amount: '',

@jaredpalmer How would you handle this, when initial values are undefined or null?

The approach of amount?: number | string; doesn't work well, if you have to do conditional rendering of form elements depending on this value.

props.values.fieldName > 1 would return an error initially as this is a string.

Although this issue was closed we had to handle this situation as well. We did this (TypeScript) :

initialValues={{myfield: '' as unknown as number}}

Not very elegant but it does the job.

I fixed it using render approach in javascript

<Field name={name}
  render={({field}) => (
    <Input
        {...field} 
        value={field.value || ''}  
     />
    )}
/>

I fixed it using render approach in javascript

<Field name={name}
  render={({field}) => (
    <Input
        {...field} 
        value={field.value || ''}  
     />
    )}
/>

Thanks Dude 鉂わ笍

Also might be useful when you want to bypass this issue in validation:

 vatId: Yup.string().nullable().transform(x => x || null)
        .min(8, __.minLength)
        .max(25, __.maxLength)
Was this page helpful?
0 / 5 - 0 ratings

Related issues

jaredpalmer picture jaredpalmer  路  3Comments

Jungwoo-An picture Jungwoo-An  路  3Comments

jaredpalmer picture jaredpalmer  路  3Comments

giulioambrogi picture giulioambrogi  路  3Comments

pmonty picture pmonty  路  3Comments