Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

151
Visualizações
'A component is changing a controlled input to be uncontrolled' and partial state update

I have a parent component that is interested in gathering a list of contacts (name+email):

import { useState } from 'react'
import AddContactFn from './components/AddContactFn'

function App() {
    const [contacts, setContacts] = useState([])

    const addNewContact = (newContact) => {
        setContacts([...contacts, newContact])
        
    }

    return (
        <div>
            <AddContactFn newContact={addNewContact} />
        </div>
    )
}

export default App

through a child component, which renders a form, with two inputs fields and an 'Add' button:


import React from 'react'
import { useState } from 'react'

export default function AddContactFn({ newContact }) {
    const [contact, setContact] = useState({name: ' ', email: ' '})

    const addContact = (e) => {
        e.preventDefault()
        if (contact.name === ' ' || contact.email === ' ') {
            return
        }
        newContact(contact)
    }

    return (
        <div>
            <h2>Add Contact</h2>
            <form onSubmit={addContact}>
                <label>Name</label>
                <input
                    type='text'
                    name='name'
                    value={contact.name}
                    placeholder='Name'
                    onChange={e => setContact({ name: e.target.value })}
                />
                <label>Email</label>
                <input
                    type='text'
                    name='email'
                    value={contact.email}
                    placeholder='Email'
                    onChange={e => setContact({ email: e.target.value })}
                />
                <button type='submit'>Add</button>
            </form>
        </div>
    )
}

I am facing two issues:

  1. Warning: 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.
  2. with both input fields containing text, every time the Add button is pressed, only the last edited value is returned to the parent (ie. either 'name' or 'email' is returned, not both)
about 4 years ago · Santiago Trujillo
3 Respostas
Responde à pergunta

0

Lets fix one at a time

  1. The warning:

Try adding a check for the input value like value={contact.name || ''} The OR || operator will check if contact.name is valid, if so use it otherwise ''

This is a side effect linked to the second issue

  1. The state:

Your onChange handler needs to add the previous object properties, currently they are not, so try onChange={e => setContact(prevState, ({...prevState, name: e.target.value }))}

By doing onChange={e => setContact({ name: e.target.value })} you are losing all the properties of the state object, and setting the state to only have a name or email property, that is why you get the warning since the name or email are lost.

Since you have two input fields (name & email) you will have to do the same on both:

<label>Name</label>
<input
   type='text'
   name='name'
   value={contact.name || ''}
   placeholder='Name'
   onChange={e => setContact(prevState => ({...prevState, name: e.target.value}))}
 />
 <label>Email</label>
 <input
   type='text'
   name='email'
   value={contact.email || ''}
   placeholder='Email'
   onChange={e => setContact(prevState => ({...prevState, email: e.target.value}))}
/>
about 4 years ago · Santiago Trujillo Relatório

0

When you use setContact({ name: e.target.value }) (or for the email) you are also setting the other field as undefined. The way I would recommend you did this is:

import React from 'react'
import { useState } from 'react'

export default function AddContactFn({ newContact }) {
    const [name, setName] = useState(' ');
    const [email, setEmail] = useState(' '); 

    const addContact = (e) => {
        e.preventDefault()
        if (name === ' ' || email === ' ') {
            return
        }
        newContact({ name, email })
    }

    return (
        <div>
            <h2>Add Contact</h2>
            <form onSubmit={addContact}>
                <label>Name</label>
                <input
                    type='text'
                    name='name'
                    value={name}
                    placeholder='Name'
                    onChange={e => setName(e.target.value)}
                />
                <label>Email</label>
                <input
                    type='text'
                    name='email'
                    value={email}
                    placeholder='Email'
                    onChange={e => setEmail(e.target.value)}
                />
                <button type='submit'>Add</button>
            </form>
        </div>
    )
}

If you want to keep the state as it currently is then you can just do e.g. setContact({ ...contact, name: e.target.value })

about 4 years ago · Santiago Trujillo Relatório

0

You are using Controlled Input where you bind the input tag value to one of contact state property and the onChange event to an arrow function that will update the contact state.

Initially, the contact state has two properties, which are name, and email.

const [contact, setContact] = useState({name: ' ', email: ' '})
...
return (
  <input
    value={contact.name}
    onChange={e => setContact({ name: e.target.value })}
  />
  <input
    value={contact.email}
    onChange={e => setContact({ email: e.target.value })}
  />
)

When the onChange event occurs, it will update the contact state with a new object that only has one property. Here, React will raise a warning as one of the input values detects the change and found that the binding reference is missing/undefined. At that moment, your input has become an Uncontrolled Input. That's why you got this error:

Warning: 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.

To fix it, you can clone the contact by using a shallow copy and update your setter like this:

onChange={e => setContact({ ...contact, name: e.target.value })}

onChange={e => setContact({ ...contact, email: e.target.value })}

So, whenever onChange event occurs, the updated contact state will always have two properties both name, and email. Of course, you can use the other technique, as long as the updated value always has consistent keys and the binding reference remain.

Here is an example:

Edit ecstatic-mclean-9vmjkj

about 4 years ago · Santiago Trujillo Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda