Reputation: 102587
I wrote a demo below for the test React
controlled component
input
feature. But It seems there is a bug.
class TestComponent extends React.Component{
constructor() {
super();
this.state = {value: 'beef'};
this.handleValueChange = this.handleValueChange.bind(this);
}
handleValueChange(e) {
this.setState({value: e.target.value});
}
render() {
return <div>
<div><input type='text' value={'hello world'} onChange={this.handleValueChange}/></div>
<div><input type='text' value={''} onChange={this.handleValueChange}/></div>
<div><input type='text' value={this.state.value} onChange={this.handleValueChange}/></div>
<div><input type='text' value={null} onChange={this.handleValueChange}/></div>
<div><input type='text' value={undefined} onChange={this.handleValueChange}/></div>
<hr/>
<div><input type='text' defaultValue={this.state.value} onChange={this.handleValueChange}/></div>
<p>{this.state.value}</p>
</div>
}
}
ReactDOM.render(
<TestComponent />,
document.body
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
The first one input
with specifying string value property. when I type something, call handleValueChange
function, and the result is hello world
+ your type thing's first character
.
The second one input
with empty string value property. when I type something, it calls handleValueChange
function, but finally, it always gives me one character.
It's weird.
updated!
I add a input
with defaultValue
, compare with value={this.state.value}
, my head is mess up..
Upvotes: 3
Views: 25156
Reputation: 725
As mentioned in a comment below your question: remove value={''}
since this will empty the input everytime it's rendered.
From the react docs, the correct way to do it is
return <div>
<input type='text' value={this.state.value} onChange={this.handleValueChange}/>
<p>{this.state.value}</p>
</div>
This way whenever you type something in the input-area, you'll update the value set in state.
If you want to render the component with a value set in state, you could use:
getInitialState() {
return { value: 'Your default value'}
}
Or, as already suggested, use defaultValue
.
Read more here: https://facebook.github.io/react/docs/forms.html
Update:
According to your updated question, I think you'll have to understand what setting a value during render function actually does. Whenever you set a value during render function, you'll "lock" the input field to be that specific value. Meaning the user input will have no effect on the rendered element. From the docs: "User input will have no effect on the rendered element because React has declared the value"
.
To solve this problem, you'll have to set the value to be something you can change dynamically, in your case that will be value
or this.state.value
. Like you've done in your third example:
<input type='text' value={this.state.value} onChange={this.handleValueChange}/>
This way React accept the value provided by the user input and will then update the value of the component thereafter.
I still think the docs specifies this pretty clearly, and I think you should read the provided documentation in my original answer.
Update 2
To clearify the part with controlled
and uncontrolled
components little bit:
A controlled
component is a component that has a value
property assigned, and will reflect the value from the user input (the value prop
).
While an uncontrolled
component does not have any value
property assigned, and will NOT reflect the value from the user input (since it does not provide any value prop
). But if you want to instantiate an uncontrolled
component with a value, you should use defaultValue
.
In your case (since you try to use a CONTROLLED component) this means that you should NOT use defaultValue
, and stick with a correct implementation of a controlled
component. That is an implementation using value={this.state.value}
.
Again I recommend reading the docs provided. It's actually not that difficult if you manage to understand the docs.
Hope this clearifies some of you problems! :)
Upvotes: 5
Reputation: 3801
The only correct controlled component
here is:
<input type='text' value={this.state.value} onChange={this.handleValueChange}/>
The others are not controlled. However, because the others are calling setState
and changing the value too, they are affecting the third input.
See what happens:
setState
with e.target.value = hello world + first character you typed
. So that will be the new value of the third inputsetState
with only one character since the value of that input is always ''
. So one character will be the new value of the third inputundefined
... That is the same as if you don't define any value there, so the fourth input will not be controlled but every time you type it's copying it's actual value to the third inputConclusion:
Upvotes: 1
Reputation: 1593
use defaultValue
instead of value
render() {
return <div>
<div><input type='text' defaultValue={'hello world'} onChange={this.handleValueChange}/></div>
<div><input type='text' defaultValue={''} onChange={this.handleValueChange}/></div>
<div><input type='text' defaultValue={this.state.value} onChange={this.handleValueChange}/></div>
<div><input type='text' defaultValue={null} onChange={this.handleValueChange}/></div>
<div><input type='text' defaultValue={undefined} onChange={this.handleValueChange}/></div>
<p>{this.state.value}</p>
</div>
}
}
Upvotes: 3