6

I have a form with three fields, the handleChange method works in the first field (DateOfBirth) but not in the (Id1) and (Id2) fields.

For some reason setState return this error when i try to change the value of the (Id1||Id2) fields.

"A component is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled (or vice versa). Decide between using a controlled or uncontrolled input element for the lifetime of the component"

import React, { Component } from 'react';

class Form extends React.Component {

    constructor(props){
        super(props);
        this.state = { DateOfBirth:'1990-01-24', Metadata: {Id1:'33813518109', Id2:'John Doe'}}
        this.handleChange = this.handleChange.bind(this);
    }

    handleChange(event) { 
        const target = event.target;
        const name = target.name;        
        var value = target.value;         

        if(name === "Id1" || name === "Id2")            
            this.setState({Metadata:{[name]: value}});     
        else
            this.setState({[name]: value});
    }  

    render() { 
        return (             
            <div>
                <form onSubmit={this.handleSubmit}>                                          
                    <input name="DateOfBirth" type="date" onChange={this.handleChange} value={this.state.DateOfBirth} />                                           
                    <input name="Id1" type="text" onChange={this.handleChange} value={this.state.Metadata.Id1} />   
                    <input name="Id2" type="text" onChange={this.handleChange} value={this.state.Metadata.Id2} />
                </form>
            </div>            
        );
    }
}

export default Form;
user2546477
  • 113
  • 1
  • 2
  • 5
  • Possible duplicate of [this.setState isn't merging states as I would expect](https://stackoverflow.com/questions/18933985/this-setstate-isnt-merging-states-as-i-would-expect) – Giorgi Moniava Mar 18 '18 at 14:47

1 Answers1

11

From react docs.

The output of the updater is shallowly merged with prevState.

Which means when you do

// name === 'Id1'
// value === 'dummy'
this.setState({Metadata:{[name]: value}});

then Metadata key in the state will have this shape:

{
  Metadata: {
    Id1: "dummy"
  }
}

Do you see the problem? Now input with Id2 receives as value undefined (this.state.Metadata.Id2 doesn't exist) which will make react throw an error about an uncontrolled component.

What you need to do to fix it is to make a full copy of nested object properties:

this.setState(prevState => ({
  Metadata:{
    ...prevState.Metadata,
    [name]: value
  }
}));
Tomasz Mularczyk
  • 34,501
  • 19
  • 112
  • 166