1

Good day,

I'm new with react.js I'm trying to create a basic data binding using onChange of the input. The problem is, I'm assigning to object with it's properties. Not directly to the property.

Now I'm receiving the error Warning: A component is changing a controlled input of type text to be uncontrolled. when I type-in a character in my inputs.

Here's my code:

interface IProps { }

interface IFloorInfo {
    id: number
    name: string,
    type: string,
    condition: string
}

interface IFloorInfoState {
    floor: IFloorInfo
}

export default class Floors extends React.Component<IProps, IFloorInfoState> {
    state: IFloorInfoState
    constructor(props: any){
        super(props)

        this.state = {
            floor: {
                id: 0,
                name: '',
                type: '',
                condition: ''
            }
        }
    }

    ...

    render() {
        return (
            <div>
                <input type="text" value={this.state.floor.name} onChange={(e)=>this.inputChanges(e)} />
                <input type="text" value={this.state.floor.type} onChange={(e)=>this.inputChanges(e)} />
                <input type="text" value={this.state.floor.condition} onChange={(e)=>this.inputChanges(e)} />
            </div>
        )
    }

}

Now this is my inputChanges method that detects if there's a changes in the input

inputChanges = (e:any) => {
    this.setState({ floor: e.target.value });
}

Thank you in advance.

Joker Bench
  • 228
  • 1
  • 6
  • 14

2 Answers2

1

The problem is with your following code. According to this code, your state will be {floor: "input value"}

inputChanges = (e:any) => {
    this.setState({ floor: e.target.value });
}

But what you actually want is

inputChanges = (e:any) => {
    // copying all values of floor from current state;
    var currentFloorState = {...this.state.floor};

    // setting the current input value from user
    var name = e.target.name;
    currentFloorState[name] = e.target.value;

    this.setState({ floor: currentFloorState });
}


As for multiple properties: You can add name property to your element and use it in your changeHandler

render() {
   return (
     <div>
       <input type="text" value={this.state.floor.name} name="floor" onChange={(e)=>this.inputChanges(e)} />
    <input type="text" value={this.state.floor.type} name="type" onChange={(e)=>this.inputChanges(e)} />
     </div>
        )
    }

For demo, you can refer this https://codesandbox.io/s/jolly-ritchie-e1z52

Sagar More
  • 466
  • 2
  • 8
0

In this code, you don't specify which property that you want to bind.

inputChanges = (e:any) => {
    this.setState({ floor: e.target.value });
}

What you can do, is something like this.

inputChanges = (e:any) => {
    this.setState({
        ...this.state,
        floor: { ... this.state.floor, [e.currentTarget.name]: e.currentTarget.value}
    })
}

Basically, you're binding whatever property that matches inside your this.state.floor object.

mark333...333...333
  • 1,270
  • 1
  • 11
  • 26
  • There are two problems with this answer: 1. `setState` performs a shallow merge of the current state with the new state so `...this.state` doesn't need to be there. 2. If the new state depends on the old state, ie you lookup `this.state` in the new state, you should pass a callback to `setState`. See https://reactjs.org/docs/react-component.html#setstate for more info – AbrahamCoding Mar 27 '20 at 09:05