65

When I render an input element within my component if i set the element "value" it become read-only but if i set the value on "defaultValue" it will never update again when i re-update my state.

Here is my code :

    import React from "react";
    export default class EditForm extends React.Component {

    editTransaction(event) {

        var transaction = this.props.transaction;

        event.preventDefault();
        var NewTransaction = {
            transactions_data: {
                amount: this.refs.amount.value
            }
        }

        this.props.editTransaction(NewTransaction, transaction.id);
    }

    closeForm() {
        this.props.closeForm();
    }

    render() {
        var {amount}=this.props.transaction;
        return (
            <div>
                <br/>
                <h4>Edit Transaction</h4>
                <div className="btn btn-danger pull-right" onClick={this.closeForm.bind(this)}>close</div>
                <div className="clearfix"></div>
                <form onSubmit={this.editTransaction.bind(this)}>
                    <div>
                        <label for="amount">Amount</label>
                        <input value={amount} onChange={(value) => this.onChange(value)} className="form-control"
                               id="amount" name="amount" type="number"
                               ref="amount"/>
                    </div>
                    <br/>
                    <br/>
                    <input className="btn btn-info" type="submit" value="submit"/>
                </form>
            </div>
        );
    }
}

and then i found out if i make an error out of this by adding onChange={(value) => this.onChange(value)} on my input element, it works properly ( it updating while the props or state is updating, and i can re-type the value), but i think this is not a proper solution, because it cause errors on my browser console. It is because "this.onChange" function does not exist.

How can this problem be solved?

1
  • 4
    React treats inputs as either controlled (value bound with prop/state), or uncontrolled (no binding on value). I'd very much recommend reading the official documentation on this several times over because this can be confusing in the beginning. You mentioned errors in the console, could you update your question with their details as well? Commented Mar 15, 2017 at 11:09

4 Answers 4

51

The reason your input doesn't work is because you need to define the onChange function which actually sets the state with the updated value. You can probably do it inline since it only needs on statement like

<input type="text" value={this.state.inputVal} onChange={(e) => {this.setState({inputVal: e.target.value})}} />

However I would recommend you to use an onChange method as you can handle multiple inputs together with it and it looks cleaner

class EditForm extends React.Component {

    constructor() {
        super();
        this.state = {
        
        }
    }
    onChange(e) {
         this.setState({[e.target.name]: e.target.value})
    }
    editTransaction(event) {

        var transaction = this.props.transaction;

        event.preventDefault();
        var NewTransaction = {
            transactions_data: {
                amount: this.refs.amount.value
            }
        }

        this.props.editTransaction(NewTransaction, transaction.id);
    }

    closeForm() {
        this.props.closeForm();
    }

    render() {
       
        return (
            <div>
                <br/>
                <h4>Edit Transaction</h4>
                <div className="btn btn-danger pull-right" onClick={this.closeForm.bind(this)}>close</div>
                <div className="clearfix"></div>
                <form onSubmit={this.editTransaction.bind(this)}>
                    <div>
                        <label for="amount">Amount</label>
                        <input value={this.state.amount} onChange={(value) => this.onChange(value)} className="form-control"
                               id="amount" name="amount" type="number"
                               ref="amount"/>
                         <input value={this.state.amount1} onChange={(value) => this.onChange(value)} className="form-control"
                               id="amount1" name="amount1" type="number"
                               ref="amount"/>
                    </div>
                    <br/>
                    <br/>
                    <input className="btn btn-info" type="submit" value="submit"/>
                </form>
            </div>
        );
    }
}

ReactDOM.render(<EditForm/>, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>
<div id="app"></div>

Sign up to request clarification or add additional context in comments.

10 Comments

Thank you for fast reply, what if i don't want to set state in this component? since i am using REDUX, all states are in my reducer, which loaded on my index.js ( the js file that calls upon this EditForm component). that's why i loaded that value from props, is there any solution that i can call inline function returning error like behavior but does't catch as error in browser console? thank you very much
Redux best practice is to have the ui state in the local state and not the redux store. However, if you want to use redux store, then you can dispatch an action in the onChange function where I set state and that action will call the reducer to change the state. Ask me if you face any problem
How would you reset it to the the original value if I have an option of cancel?
@SaifAliKhan By Reset do you mean whatever the changes you have made revert them back
@ShubhamKhatri Yes. I am facing this issue that I have set the value at the first time, now I am changing that value through setState on onChange function. Now I also have an option to cancel. How will I get that value which was initially set on cancel? since it is updated with new value?
|
5

When you set the value on an input in React, its value will never change (unless you specify an onChange handler)

<input type="text" name="address" value="Europe" />

The end result of this element is a textbox with the value Europe that cannot be changed, so effectively, it's a read-only textbox.

Instead, when you want to provide a default value for your input field, you should use the defaultValue attribute as follows:

<input type="text" name="address" defaultValue="Europe" />

So just replace the value attribute with defaultValue

Comments

4

You need to define an onChange method, if you are using redux that could be an action that updates your component state via a reducer. An easier method using simple state with es6 is shown below. Furthermore, you are getting the value from the input field via ref which is discouraged by Facebook. This should also give you an error because you are trying to control and uncontrolled component.

Here's a link to the form documentation for further reading.

class Foo extends React.Component { 
  constructor(props) {
    super(props);
    this.state = ({
      inputVal: '',
    });
  }
  onChange = (e) => {
    this.setState({ [e.target.name]: e.target.value });
  }
  handleSubmit = (event) => {
    event.preventDefault();
    console.log(this.state.inputVal);
  }
  render() {
    return (
      <div>
        <input type="text" value={this.state.inputVal} onChange={this.onChange} />
      </div>
    );
  }
}

1 Comment

Thank you for fast reply, what if i don't want to set state in this component? since i am using REDUX, all states are in my reducer, which loaded on my index.js ( the js file that calls upon this EditForm component). that's why i loaded that value from props, is there any solution that i can call inline function returning error like behavior but does't catch as error in browser console? thank you very much
0

I was tinkering with this and found a really simple solution:

class App extends Component {

constructor(props) {
    super(props)
    this.enterText = this.enterText.bind(this)
    this.state = {
        userNameText: 'user name',
        pswdText: 'pswd',
        textEntry: false
    }
}

async enterText() {
    if (!this.state.textEntry) {
        await this.clearText()
        this.setState({textEntry: true})
    }
}

clearText() {
    this.setState({
        userNameText: '',
        pswdText: ''
    })
}

render() {
    return (
        <div className="App">
            <div className="App-header">
                <h1 className="App-title">
                    Welcome
                </h1>
            </div>
            <div className="login-fields">
                <LoginFields userNameText={this.state.userNameText} pswdText={this.state.pswdText} onFocus={this.enterText} />
            </div>
        </div>
    )
}
}

So, upon initial render(), the state of the fields are whatever is hard coded in the constructor. When the user clicks in either the name or pswd box (these could easily be separated), both are cleared with clearText() and then both are set to null with allowText(). Once set to null, the user input text is accepted.

Make sure the LoginFields component has this in the input:

onFocus={this.props.onFocus}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.