0

I have input whose value is changed by button element. I need a way to call onChange of that input. I have some validation that has to be done when the value is changed.

I found some answers on StackOverflow but they are using dispatch which they didn't explain properly or I didn't use it properly.

But onChange is not called when the state is updated. Here is what I have by now

<div className="input-group">
  <input type="text" className="form-control quantity-field text-center" onChange={(e) => this.handleChange(e)} value={this.state.quantity}/>
  <button onClick={(e) => this.handleIncDec(e)}>+</button>
</div>
  handleIncDec = (e) => {
    e.preventDefault()

    this.setState({
      quantity: this.state.quantity += 1
    })
  }

handleChange = e => {
    console.log(e.target.value);

    const re = /^[0-9\b]+$/;
    if (e.target.value === '' || re.test(e.target.value)) {
      this.setState({
        quantity: e.target.value
      })
    }
  };

The value of the input is updated correctly as it should be but onChange is never called unless I updated value directly in that input and not with button, which is not what I want.

If you need any other info to tell me and I will provide it.

Mileta Dulovic
  • 1,036
  • 1
  • 14
  • 33
  • Try this: https://stackoverflow.com/questions/42550341/react-trigger-onchange-if-input-value-is-changing-by-state – new Q Open Wid Nov 21 '19 at 23:07
  • Try `quantity: this.state.quantity + 1` instead so you won't mutate state \and if you have an input that is a number try ` – HMR Nov 21 '19 at 23:10
  • @zixuan no it is not working. I tried that one HMR read question again. Value is updated properly which mean setState is working as it should. onChange is never triggered. – Mileta Dulovic Nov 21 '19 at 23:14

1 Answers1

2

The onChange-event will by default only be called on userinputs. Most of the time you can avoid trigger the onChange event programatically by using lifecycles-methods like ComponentDidUpdate or similar in React. In your example it looks to me like you only need to validte input from two different sources, so I would suggest an easier implementation.

Can you create a validationfunction instead that you can use on both handleChange and handleInDec?

You should also avoid updating state based on previous state with this.setState({ quantity: this.state.quantity += 1}), as you are not guaranteed that state always will be updated (Remember, setState is asynchronous). Instead use a the return value from setState that guarantees updated state-values

class Test extends React.Component {
  constructor() {
    super();

    this.state = {
      quantity: 0
    };
  }

  handleIncDec = e => {
    e.preventDefault();

    this.setState(prevState => {
      var newQuantity = prevState.quantity + 1;
      if (this.isValidInputData(newQuantity)) {
        return {
          quantity: newQuantity
        };
      }
    });
  };

  handleChange = e => {
    if (this.isValidInputData(e.target.value)) {
      this.setState({
        quantity: e.target.value
      });
    }
  };

  isValidInputData = newQuantity => {
    const re = /^[0-9\b]+$/;
    return newQuantity === "" || re.test(newQuantity);
  };

  render() {
    return (
      <div className="input-group">
        <input
          type="text"
          className="form-control quantity-field text-center"
          onChange={this.handleChange}
          value={this.state.quantity}
        />
        <button onClick={this.handleIncDec}>+</button>
      </div>
    );
  }
}

ReactDOM.render(<Test />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
christianeide
  • 417
  • 2
  • 13