Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using toFloat and setState on number input in React

I have a number input field that sets the new value through the state when the user is typing. This works fine. If I however add decimals to it (toFixed), the cursor will jump to the end of the input after typing only one digit. This happens when I, for example, delete what is there and type something else. If I instead add a digit between the first one and the decimal point, the cursor does not jump. Has anyone experienced this before, and what was the solution?

Fiddle to show the problem: https://jsfiddle.net/Inverness/k04yvq1u/

constructor() {
    super()

    this.state = {
      number: 1
    }

    this.handleInputChange = this.handleInputChange.bind(this)
  }

  handleInputChange(event) {
    console.log(event.target.value)
    this.setState({
      number: event.target.value
    })
  }

  render() {
    return (
      <div>
        <input
          type="number"
          value={ parseFloat(this.state.number).toFixed(2) }
          onChange={ this.handleInputChange }
          />
      </div>
    )
  }
like image 411
Ellinor Avatar asked Aug 22 '19 21:08

Ellinor


2 Answers

I've had success using the onBlur event handler to simply format the input field to the way you need it after the user has finished typing.

Adding a bit of info in the form field label that the input will be limited to 2 decimal spaces should help alleviate any UX issues with changing a value that the user has provided.

 constructor() {
    super()

    this.state = {
      number: 1
    }

    this.handleInputChange = this.handleInputChange.bind(this)
    this.formatInput = this.formatInput.bind(this)
  }

  handleInputChange(event) {
    console.log(event.target.value)
    this.setState({
      number: event.target.value
    })
  }

  formatInput() {
    const num = this.state.number
    this.setState({
        number: parseFloat(num).toFixed(2)
    })
  }

  render() {
    return (
      <div>
        <input
          type="number"
          value={ this.state.number }
          onChange={ this.handleInputChange }
          onBlur={ this.formatInput }
          />
      </div>
    )
 }

JSFiddle

like image 150
Mike Abeln Avatar answered Nov 19 '22 03:11

Mike Abeln


I had something similar to this happen. The main issue here is that you are re-formatting what they type in as they type. Since react is using javascript to change the value, the browser can't guess where the cursor is supposed to go, so it just jumps to the end. The way I solved this was by only updating when the user enters something that matches the toFixed format. Something roughly equivalent that should work is setting value={this.state.number} on your input. Then the on change should look something like this:

handleInputChange = e => {
  let val = e.target.value
  if (/^[\d]*\.?[\d]{0,2}$/.test(val)) {
    this.setState({number: val})
  }
}

That way the input value still is constrained to be a two-decimal floating point format, but the user has more freedom to type it.

like image 37
ryan28561 Avatar answered Nov 19 '22 04:11

ryan28561