Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React: Number input with no negative, decimal, or zero value values

Consider an input of type number, I would like this number input to only allow a user to enter one positive, non-zero, integer (no decimals) number. A simple implementation using min and step looks like this:

class PositiveIntegerInput extends React.Component {
  render () {  	
  	return <input type='number' min='1' step='1'></input>
  }
}

ReactDOM.render(
  <PositiveIntegerInput />,
  document.getElementById('container')
)
<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>
<p>
  Try to input a decimal or negative number or zero:
</p>
<div id="container"></div>

The above code works fine if a user sticks to ONLY clicking the up/down arrows in the number input, but as soon a the user starts using the keyboard they will have no problem entering numbers like -42, 3.14 and 0

Ok, lets try adding some onKeyDown handling to disallow this loophole:

class PositiveIntegerInput extends React.Component {
	constructor (props) {
    super(props)
    this.handleKeypress = this.handleKeypress.bind(this)
  }

  handleKeypress (e) {
    const characterCode = e.key
    if (characterCode === 'Backspace') return

    const characterNumber = Number(characterCode)
    if (characterNumber >= 0 && characterNumber <= 9) {
      if (e.currentTarget.value && e.currentTarget.value.length) {
        return
      } else if (characterNumber === 0) {
        e.preventDefault()
      }
    } else {
      e.preventDefault()
    }
  }

  render () {  	
    return (
      <input type='number' onKeyDown={this.handleKeypress} min='1' step='1'></input>
    )
  }
}

ReactDOM.render(
    <PositiveIntegerInput />,
    document.getElementById('container')
)
<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>

<p>
  Try to input a decimal or negative number or zero:
</p>
<div id="container"></div>

Now everything almost appears to work as desired. However if a user highlights all the digits in the text input and then types over this selection with a 0 the input will allow 0 to be entered as a value.

To fix this issue I added an onBlur function that checks if the input value is 0 and if so changes it to a 1:

class PositiveIntegerInput extends React.Component {
	constructor (props) {
  	super(props)
    this.handleKeypress = this.handleKeypress.bind(this)
    this.handleBlur = this.handleBlur.bind(this)
  }
  
  handleBlur (e) {
    if (e.currentTarget.value === '0') e.currentTarget.value = '1'
  }

	handleKeypress (e) {
    const characterCode = e.key
    if (characterCode === 'Backspace') return

    const characterNumber = Number(characterCode)
    if (characterNumber >= 0 && characterNumber <= 9) {
      if (e.currentTarget.value && e.currentTarget.value.length) {
        return
      } else if (characterNumber === 0) {
        e.preventDefault()
      }
    } else {
			e.preventDefault()
    }
  }

  render () {  	
  	return (
    	<input
        type='number'
        onKeyDown={this.handleKeypress}
        onBlur={this.handleBlur}
        min='1'
        step='1' 
      ></input>
    )
  }
}

ReactDOM.render(
  <PositiveIntegerInput />,
  document.getElementById('container')
);
<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>

<p>
  Try to input a decimal or negative number or zero:
</p>
<div id="container"></div>

Is there a better way to implement a number input with this type of criteria? It seems pretty crazy to write all this overhead for an input to allow only positive, non-zero integers... there must be a better way.

like image 700
Cumulo Nimbus Avatar asked Feb 09 '18 18:02

Cumulo Nimbus


People also ask

How do you prevent negative values in input type numbers in react JS?

You can add an onKeyPress listener to the input element and that will be triggered before the onChange and call a function that will prevent default behaviour when the minus button is pressed. Note that this will still allow negative values to be pasted into the input.

How do you allow only numbers in input field React?

Basic idea is: Use controlled component (use value and onChange property of input field), and inside onChange handle check whether the entered value is proper number or not. Update the state only when entered value is a valid number.

Does input type number allow negative?

The number input type can accept both positive and negative integers as well as floating point numbers.


1 Answers

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}
            />
        );
     }
}
like image 141
Anthony Avatar answered Oct 02 '22 04:10

Anthony