Why am I getting the following warning after submitting a form using Formik: A component is changing a controlled input to be uncontrolled

JackFrost

I am getting the following warning when submitting a form using Formik: "A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component."

I know that there are multiple questions already answered about this warning and I generally know what it means. But I can just not understand where it happens, where or when does the input become uncontrolled.

I already tried to delete formik.resetForm() and formik.setFieldValue() within the handleRegister function because I thought maybe they are setting an input to undefined, but it did not change anything.

This is the code of my register component:

const Register: NextPage = () => {
  const { data: session, status } = useSession()
  if (session) {
    const router = useRouter()
    router.push('/')
  }

  const [error, setError] = useState('')
  const [success, setSuccess] = useState('')

  const handleRegister = async (values: RegisterInputsData) => {
    setError('')
    setSuccess('')

    const registerInputsData = {
      ...values
    }

    if (!registerInputsData.company.website) {
      delete registerInputsData.company.website
    }

    if (!registerInputsData.company.socials) {
      delete registerInputsData.company.socials
    }

    const registerInputsDataJSON = JSON.stringify(registerInputsData)

    const endpoint: RequestInfo = '/api/register'
    const options: RequestInit = {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json'
      },
      body: registerInputsDataJSON
    }

    try {
      const response: Response = await fetch(endpoint, options)

      if (!response.ok) {
        const { error } = await response.json()
        if (typeof error === 'object' && error.name === 'ValidationError') {
          const formikError = yupToFormErrors(error)
          return formik.setErrors(formikError)
        }
        return setError(error)
      }
      formik.resetForm()
      setSuccess(registerInputsData.user.email)
    } catch (error) {
      setError('Register failed')
    } finally {
      formik.setFieldValue('user.password', formik.initialValues.user.password, false)
      formik.setFieldValue('user.passwordConfirmation', formik.initialValues.user.passwordConfirmation, false)
      formik.setFieldValue('user.pin', formik.initialValues.user.pin, false)
      formik.setFieldValue('user.pinConfirmation', formik.initialValues.user.pinConfirmation, false)
    }
  }

  const formik = useFormik({
    initialValues: {
      user: {
        email: '',
        password: '',
        passwordConfirmation: '',
        pin: '',
        pinConfirmation: ''
      },

      company: {
        name: '',
        title: '',
        firstName: '',
        lastName: '',
        street: '',
        houseNumber: '',
        postcode: '',
        city: '',
        country: '',
        countryCode: '',
        callNumber: '',
        email: '',
        website: '',
        socials: ''
      }
    },
    validationSchema: registerYupSchema,
    onSubmit: async (values) => {
      await handleRegister(values)
    }
  })

  return status === 'loading' ? <></> : (
    <>
      <Navbar />

      <form onSubmit={formik.handleSubmit} noValidate className={styles.form}>

        <div className={styles.section}>
          <h3 className={styles.h3}>User</h3>

          <div className={styles.group}>
            <input
              name='user.email'
              type='text'
              value={formik.values.user.email}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Email<span>*</span></label>
            {formik.touched.user?.email && formik.errors.user?.email && <div className={styles.error}>{formik.errors.user.email}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='user.password'
              type='password'
              value={formik.values.user.password}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Password<span>*</span></label>
            {formik.touched.user?.password && formik.errors.user?.password && <div className={styles.error}>{formik.errors.user.password}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='user.passwordConfirmation'
              type='password'
              value={formik.values.user.passwordConfirmation}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Password Confirmation<span>*</span></label>
            {formik.touched.user?.passwordConfirmation && formik.errors.user?.passwordConfirmation && <div className={styles.error}>{formik.errors.user.passwordConfirmation}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='user.pin'
              type='password'
              value={formik.values.user.pin}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Pin<span>*</span></label>
            {formik.touched.user?.pin && formik.errors.user?.pin && <div className={styles.error}>{formik.errors.user.pin}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='user.pinConfirmation'
              type='password'
              value={formik.values.user.pinConfirmation}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Pin Confirmation<span>*</span></label>
            {formik.touched.user?.pinConfirmation && formik.errors.user?.pinConfirmation && <div className={styles.error}>{formik.errors.user.pinConfirmation}</div>}
          </div>
        </div>

        <div className={styles.section}>
          <h3 className={styles.h3}>Company</h3>

          <div className={styles.group}>
            <input
              name='company.name'
              type='text'
              value={formik.values.company.name}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Company Name<span>*</span></label>
            {formik.touched.company?.name && formik.errors.company?.name && <div className={styles.error}>{formik.errors.company.name}</div>}
          </div>

          <div className={styles.group}>
            <select
              name='company.title'
              value={formik.values.company.title}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.select}
              aria-label='Title'
            >
              <option value='' hidden className={styles.option}></option>
              <option value='Ms.' className={styles.option}>Ms.</option>
              <option value='Mr.' className={styles.option}>Mr.</option>
            </select>
            <label className={styles.label}>Title<span>*</span></label>
            {formik.touched.company?.title && formik.errors.company?.title && <div className={styles.error}>{formik.errors.company.title}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='company.firstName'
              type='text'
              value={formik.values.company.firstName}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>First Name<span>*</span></label>
            {formik.touched.company?.firstName && formik.errors.company?.firstName && <div className={styles.error}>{formik.errors.company.firstName}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='company.lastName'
              type='text'
              value={formik.values.company.lastName}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Last Name<span>*</span></label>
            {formik.touched.company?.lastName && formik.errors.company?.lastName && <div className={styles.error}>{formik.errors.company.lastName}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='company.street'
              type='text'
              value={formik.values.company.street}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Street<span>*</span></label>
            {formik.touched.company?.street && formik.errors.company?.street && <div className={styles.error}>{formik.errors.company.street}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='company.houseNumber'
              type='text'
              value={formik.values.company.houseNumber}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>House Number<span>*</span></label>
            {formik.touched.company?.houseNumber && formik.errors.company?.houseNumber && <div className={styles.error}>{formik.errors.company.houseNumber}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='company.postcode'
              type='text'
              value={formik.values.company.postcode}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Postcode<span>*</span></label>
            {formik.touched.company?.postcode && formik.errors.company?.postcode && <div className={styles.error}>{formik.errors.company.postcode}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='company.city'
              type='text'
              value={formik.values.company.city}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>City<span>*</span></label>
            {formik.touched.company?.city && formik.errors.company?.city && <div className={styles.error}>{formik.errors.company.city}</div>}
          </div>

          <div className={styles.group}>
            <select
              name='company.country'
              value={formik.values.company.country}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.select}
              aria-label='Country'
            >
              <option value='' hidden className={styles.option}></option>
              {Object.keys(countries).map((key, i) => (
                <option value={countries[key].name} key={i} className={styles.option}>{countries[key].name}</option>
              ))}
            </select>
            <label className={styles.label}>Country<span>*</span></label>
            {formik.touched.company?.country && formik.errors.company?.country && <div className={styles.error}>{formik.errors.company.country}</div>}
          </div>

          <div className={styles.group}>
            <select
              name='company.countryCode'
              value={formik.values.company.countryCode}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.select}
              aria-label='Country Code'
            >
              <option value='' hidden className={styles.option}></option>
              {Object.keys(countries).map((key, i) => (
                <option value={'+' + countries[key].phone} key={i} className={styles.option}>{countries[key].emoji + ' +' + countries[key].phone}</option>
              ))}
            </select>
            <label className={styles.label}>Country Code<span>*</span></label>
            {formik.touched.company?.countryCode && formik.errors.company?.countryCode && <div className={styles.error}>{formik.errors.company.countryCode}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='company.callNumber'
              type='text'
              value={formik.values.company.callNumber}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Call Number<span>*</span></label>
            {formik.touched.company?.callNumber && formik.errors.company?.callNumber && <div className={styles.error}>{formik.errors.company.callNumber}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='company.email'
              type='text'
              value={formik.values.company.email}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Email<span>*</span></label>
            {formik.touched.company?.email && formik.errors.company?.email && <div className={styles.error}>{formik.errors.company.email}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='company.website'
              type='text'
              value={formik.values.company.website}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Website</label>
            {formik.touched.company?.website && formik.errors.company?.website && <div className={styles.error}>{formik.errors.company.website}</div>}
          </div>

          <div className={styles.group}>
            <input
              name='company.socials'
              type='text'
              value={formik.values.company.socials}
              onChange={formik.handleChange}
              onBlur={formik.handleBlur}
              className={styles.input}
            />
            <label className={styles.label}>Socials</label>
            {formik.touched.company?.socials && formik.errors.company?.socials && <div className={styles.error}>{formik.errors.company.socials}</div>}
          </div>
        </div>

        <button type='submit' disabled={formik.isSubmitting} className={styles.button}>Register</button>
      </form>

      {formik.isSubmitting && <div className={styles.lds_ring}><div></div><div></div><div></div><div></div></div>}
      {!formik.isSubmitting && error && <div className={styles.message}>{error}</div>}
      {!formik.isSubmitting && success && <div className={styles.message}>Registration successful - A verification email has been sent to the following email: {success}</div>}
    </>
  )
}

export default Register

I am thankful for any help I can get.

Jakob Basshunter Widmann

The values parameter in this case is a direct reference to an object. Although you are using the spread operators to create a deep copy of it, as there are nested objects within values you need to use the spread operator on these too. Otherwise they will be shallow copies.

And as you are deleting properties of them (website, socials) if the respecting inputs remain empty, the inputs become uncontrolled.

The solution could look like this:

const registerInputsData = {
   user: {...values.user},
   company: {...values.company}
}

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

React Formik Warning: A component is changing an uncontrolled input to be controlled

Warning- A component is changing a controlled input to be uncontrolled

Warning: A component is changing an uncontrolled input to be controlled

Getting "A component is changing an uncontrolled input to be controlled (...)" when using KeyboardDatePicker

Formik component changing an uncontrolled input of type text to be controlled

Email Input Warning - A component is changing a controlled input of type text to be uncontrolled

React Input Warning: A component is changing a controlled input of type text to be uncontrolled

Warning: A component is changing an uncontrolled input of type text to be controlled

Warning: A component is changing a controlled input of type undefined to be uncontrolled

shadcn Input + Form + Zod "A component is changing an uncontrolled input to be controlled"

On clearing input label I'm getting a component is changing an uncontrolled input of type text to be controlled

A component is changing an uncontrolled input of type text to be controlled?

A component is changing an uncontrolled input of type text to be controlled

A component is changing an uncontrolled input of type email to be controlled

Generate input/label element in React child component conditionally but got a 'component is changing an uncontrolled input to be controlled.' warning

Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined,

(ReactJS) "Warning: A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined ..."

Warning: A component is changing a controlled input to be uncontrolled. This is likely caused by the value changing from a defined to undefined

Can you help me to get rid of this? Warning: A component is changing an uncontrolled input of type search to be controlled

React input, I defined the state into initial state but still get warning changing uncontrolled to controlled input

Why am I getting null object after submitting form in Spring?

Trying to dynamically create number of fields in a form - A component is changing a controlled input to be uncontrolled

A component is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled

ReactJS Warning: TextField is changing an uncontrolled input of type text to be controlled

Warning: TextField is changing a controlled input of type text to be uncontrolled

'A component is changing a controlled input to be uncontrolled' and partial state update

React a component is changing an uncontrolled input of type checkbox to be controlled

A component is changing an uncontrolled input of type text to be controlled error in ReactJS

ReactJS, A component is changing an uncontrolled input of type number to be controlled