React:没有负值、小数或零值的数字输入

Posted

技术标签:

【中文标题】React:没有负值、小数或零值的数字输入【英文标题】:React: Number input with no negative, decimal, or zero value values 【发布时间】:2018-07-20 13:44:57 【问题描述】:

考虑number 类型的输入,我希望这个数字输入只允许用户输入一个正数非零整数(无小数)数字。使用minstep 的简单实现如下所示:

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>

如果用户坚持只点击数字输入中的向上/向下箭头,上面的代码可以正常工作,但是一旦用户开始使用键盘,他们输入像-423.14 这样的数字就没有问题和0

好的,让我们尝试添加一些onKeyDown 处理来阻止这个漏洞:

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>

现在一切似乎都按预期工作了。但是,如果用户突出显示文本输入中的所有数字,然后使用 0 覆盖此选择,则输入将允许将 0 作为值输入。

为了解决这个问题,我添加了一个onBlur 函数来检查输入值是否为0,如果是则将其更改为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>

有没有更好的方法来使用这种类型的标准来实现数字输入? 将所有这些开销写入输入以只允许正数,这似乎很疯狂,非零整数...一定有更好的方法。

【问题讨论】:

您是在寻找验证还是限制输入?通过验证,您可以添加最少的通知用户他的输入无效,这可以给他更多的信息,而不是简单地限制他?我的意思是,在您的最后一个示例中,我仍然可以在无效输入中使用鼠标右键并过去 :) 对于诸如“您家中的活人数量”之类的内容,我认为没有必要指定“不允许使用分数或负数的人”。 【参考方案1】:

如果您使用组件状态中的值作为受控输入进行操作,则可以阻止更新状态 onChange 如果它不符合您的条件。例如

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
            />
        );
     

【讨论】:

这是实现所需最终结果的一种简单的好方法,但我的最终目标是甚至不允许用户到达输入中输入无效字符的地步跨度> 这实际上不会通过按键输入任何非数字或“0”(仍然可以复制粘贴文本,但模糊可能会有所帮助)【参考方案2】:

这是 React Bootstrap 中的数字微调器植入。它只接受正整数,您可以设置最小值、最大值和默认值。

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" />

【讨论】:

【参考方案3】:

这就是数字输入的工作原理。为了简化代码,您可以尝试使用validity state(如果您的目标浏览器支持)

onChange(e) 
    if (!e.target.validity.badInput) 
       this.setState(Number(e.target.value))
    

【讨论】:

【参考方案4】:

当我只需要允许正数时,我遇到了类似的问题,在 ***(https://***.com/a/34783480/5646315) 上找到另一个问题的解决方案。

我为react-final-form 实现的示例代码。 P.S:这不是最优雅的解决方案。

onKeyDown: (e: React.KeyboardEvent) => 
                  if (!((e.keyCode > 95 && e.keyCode < 106) || (e.keyCode > 47 && e.keyCode < 58) || e.keyCode === 8)) 
                    e.preventDefault()
                  
                ,

【讨论】:

【参考方案5】:
class BasketItem extends React.Component 
  constructor(props) 
    super(props);
    this.state = 
      countBasketItem: props.qnt,
    ;
  

  componentDidMount() 
    const $ = window.$;
    // using jquery-styler-form(bad practice)
    $('input[type="number"]').styler();
    // minus 1
    $(`#basket_$this.props.id .jq-number__spin.minus`).click(() => 
      if (this.state.countBasketItem > 1) 
        this.setState( countBasketItem: +this.state.countBasketItem - 1 );
        this.setCountProduct();
      
    );
    // plus 1
    $(`#basket_$this.props.id .jq-number__spin.plus`).click(() => 
      this.setState( countBasketItem: +this.state.countBasketItem + 1 );
      this.setCountProduct();
    );
  

  onChangeCount = (e) => 
    let countBasketItem = +e.target.value
    countBasketItem = (countBasketItem === 0) ? '' : (countBasketItem > 999) ? 999 : countBasketItem;
    this.setState( countBasketItem )
  ;

  onBlurCount() 
    // number empty
    if (+this.state.countBasketItem == 0 || isNaN(+this.state.countBasketItem)) 
      this.setState( countBasketItem: 1 );
    
    this.setCountProduct();
  

  setCountProduct = (colrKey = this.props.colr.key, idProduct = this.props.product.id, qnt) => 
    qnt = +this.state.countBasketItem || 1; // if don't work setState
    this.props.basket.editCountProduct(idProduct, colrKey, qnt); // request on server
  ;

  render() 
    return;
    <input
      type="number"
      className="number"
      min="1"
      value=this.state.countBasketItem
      onChange=this.onChangeCount.bind(this)
      // onFocused
      onBlur=this.onBlurCount.bind(this)
      // input only numbers
      onKeyPress=(event) => 
        if (!/[0-9]/.test(event.key)) 
          event.preventDefault();
        
      
    />;
  

【讨论】:

这个答案很好。可以通过解释如何解决问题/更改的内容以及原因来改进它。【参考方案6】:

这不是一个反应问题,而是一个 html 问题,你可以在这里看到 https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/number 我已经做了一个无状态的例子,你可以在这里看到 https://codesandbox.io/s/l5k250m87

【讨论】:

你绝对是对的。这不仅仅是一个反应问题,它通常与 html 输入元素有关。像这样的 React 示例似乎是了解概念的最佳方式 不幸的是:( 如果有人使用 react 并想在这种情况下解决这个问题,问题就是反应问题。在最好的情况下,告诉人们他们错误地分类了他们的问题是没有帮助的。此外,您的示例是 404。 我可以在您的代码框示例中输入任何数字,但此解决方案不起作用。

以上是关于React:没有负值、小数或零值的数字输入的主要内容,如果未能解决你的问题,请参考以下文章

在 Swift 中将小数点后的逗号和零值添加到千位后的数字字符串中

JavaScript 之 限制输入框值的整数小数位数

JavaScript 之 限制输入框值的整数小数位数

JavaScript 之 限制输入框值的整数小数位数

sort

怎么让输入框只能输入数字和小数点?