Lin Du
Lin Du

Reputation: 102587

React controlled component input value with empty string

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

Answers (3)

chrisv
chrisv

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

Ferran Negre
Ferran Negre

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:

  • First input will call setState with e.target.value = hello world + first character you typed. So that will be the new value of the third input
  • Second input will call setState with only one character since the value of that input is always ''. So one character will be the new value of the third input
  • Fourth input the same as the second one
  • Last input, since the value is set to undefined... 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 input

Conclusion:

  • To get a controlled input, always set the value to something you control it's change (state or props).
  • Be careful when changing the same part of the state using different inputs... It becomes really complicated to reason about.

Upvotes: 1

Md.Estiak Ahmmed
Md.Estiak Ahmmed

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

Related Questions