Solution Working in the Year 2020 and 2021:
I tried the other solutions and nothing worked. This is because of input logic in React.js has been changed. For detail, you can see this link: https://hustle.bizongo.in/simulate-react-on-change-on-controlled-components-baa336920e04.
In short, when we change the value of input by changing state and then dispatch a change event then React will register both the setState and the event and consider it a duplicate event and swallow it.
The solution is to call native value setter on input (See setNativeValue function in following code)
Example Code
import React, { Component } from 'react'
export class CustomInput extends Component {
inputElement = null;
// THIS FUNCTION CALLS NATIVE VALUE SETTER
setNativeValue(element, value) {
const valueSetter = Object.getOwnPropertyDescriptor(element, 'value').set;
const prototype = Object.getPrototypeOf(element);
const prototypeValueSetter = Object.getOwnPropertyDescriptor(prototype, 'value').set;
if (valueSetter && valueSetter !== prototypeValueSetter) {
prototypeValueSetter.call(element, value);
} else {
valueSetter.call(element, value);
}
}
constructor(props) {
super(props);
this.state = {
inputValue: this.props.value,
};
}
addToInput = (valueToAdd) => {
this.setNativeValue(this.inputElement, +this.state.inputValue + +valueToAdd);
this.inputElement.dispatchEvent(new Event('input', { bubbles: true }));
};
handleChange = e => {
console.log(e);
this.setState({ inputValue: e.target.value });
this.props.onChange(e);
};
render() {
return (
<div>
<button type="button" onClick={() => this.addToInput(-1)}>-</button>
<input
readOnly
ref={input => { this.inputElement = input }}
name={this.props.name}
value={this.state.inputValue}
onChange={this.handleChange}></input>
<button type="button" onClick={() => this.addToInput(+1)}>+</button>
</div>
)
}
}
export default CustomInput
Result