React: Number input with no negative, decimal, or zero value values
If you did it as a controlled input with the value in component state, you could prevent updating state onChange if it didn't meet your criteria. e.g.
class PositiveInput extends React.Component { state = { value: '' } onChange = e => { //replace non-digits with blank const value = e.target.value.replace(/[^\d]/,''); if(parseInt(value) !== 0) { this.setState({ value }); } } render() { return ( <input type="text" value={this.state.value} onChange={this.onChange} /> ); }}
Here's a number spinner implantation in React Bootstrap. It only accepts positive integers and you can set min, max and default values.
class NumberSpinner extends React.Component { constructor(props, context) { super(props, context); this.state = { oldVal: 0, value: 0, maxVal: 0, minVal: 0 }; this.handleIncrease = this.handleIncrease.bind(this); this.handleDecrease = this.handleDecrease.bind(this); this.handleChange = this.handleChange.bind(this); this.handleBlur = this.handleBlur.bind(this); } componentDidMount() { this.setState({ value: this.props.value, minVal: this.props.min, maxVal: this.props.max }); } handleBlur() { const blurVal = parseInt(this.state.value, 10); if (isNaN(blurVal) || blurVal > this.state.maxVal || blurVal < this.state.minVal) { this.setState({ value: this.state.oldVal }); this.props.changeVal(this.state.oldVal, this.props.field); } } handleChange(e) { const re = /^[0-9\b]+$/; if (e.target.value === '' || re.test(e.target.value)) { const blurVal = parseInt(this.state.value, 10); if (blurVal <= this.state.maxVal && blurVal >= this.state.minVal) { this.setState({ value: e.target.value, oldVal: this.state.value }); this.props.changeVal(e.target.value, this.props.field); } else { this.setState({ value: this.state.oldVal }); } } } handleIncrease() { const newVal = parseInt(this.state.value, 10) + 1; if (newVal <= this.state.maxVal) { this.setState({ value: newVal, oldVal: this.state.value }); this.props.changeVal(newVal, this.props.field); }; } handleDecrease() { const newVal = parseInt(this.state.value, 10) - 1; if (newVal >= this.state.minVal) { this.setState({ value: newVal, oldVal: this.state.value }); this.props.changeVal(newVal, this.props.field); }; } render() { return ( < ReactBootstrap.ButtonGroup size = "sm" aria-label = "number spinner" className = "number-spinner" > < ReactBootstrap.Button variant = "secondary" onClick = { this.handleDecrease } > - < /ReactBootstrap.Button> < input value = { this.state.value } onChange = { this.handleChange } onBlur = { this.handleBlur } /> < ReactBootstrap.Button variant = "secondary" onClick = { this.handleIncrease } > + < /ReactBootstrap.Button> < / ReactBootstrap.ButtonGroup > ); }}class App extends React.Component { constructor(props, context) { super(props, context); this.state = { value1: 1, value2: 12 }; this.handleChange = this.handleChange.bind(this); } handleChange(value, field) { this.setState({ [field]: value }); } render() { return ( <div> <div>Accept numbers from 1 to 10 only</div> < NumberSpinner changeVal = { () => this.handleChange } value = { this.state.value1 } min = { 1 } max = { 10 } field = 'value1' / > <br /><br /> <div>Accept numbers from 10 to 20 only</div> < NumberSpinner changeVal = { () => this.handleChange } value = { this.state.value2 } min = { 10 } max = { 20 } field = 'value2' / > <br /><br /> <div>If the number is out of range, the blur event will replace it with the last valid number</div> </div>); }}ReactDOM.render( < App / > , document.getElementById('root'));
.number-spinner { margin: 2px;}.number-spinner input { width: 30px; text-align: center;}
<script src="https://unpkg.com/react@16/umd/react.development.js" crossorigin></script><script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js" crossorigin></script><script src="https://unpkg.com/react-bootstrap@next/dist/react-bootstrap.min.js" crossorigin></script><link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/latest/css/bootstrap.min.css" crossorigin="anonymous"><div id="root" />
That's how number input works. To simplify the code you could try to use validity state (if your target browsers support it)
onChange(e) { if (!e.target.validity.badInput) { this.setState(Number(e.target.value)) }}