React Component and Invalid Input

2019-04-12 05:08发布

问题:

I have the following react component:

var App = React.createClass({
    getInitialState: function() {
        return {value: 4.5}
    },
    change: function(event) {
        this.setState({value: parseFloat(event.target.value)});
    },
    render: function() {
        return <input type="number" value={this.state.value} onChange={this.change}/>;
    }
});

React.render(<App/>, document.body);

You can see it here: http://jsfiddle.net/2hauj2qg/

The problem is that if I want to type in a number like: "4.7". When the user enters, "4.", it becomes "4", due to being converted to a float in back. But this interrupts what the user was typing. What's the recommended way of solving this problem?

回答1:

As imjared mentioned, it's because you're using parseFloat

this.setState({value: parseFloat(event.target.value)});

Instead, you may wish to only allow digits and the decimal. It stays stored as a string and never changes their input, but they're prevented from typing things like letters and spaces.

var nonNumericRegex = /[^0-9.]+/g;

this.setState({value: event.target.value.replace(nonNumericRegex, "")});

To allow negative numbers you need to do this:

this.setState({
    value: event.target.value
        .replace(nonNumericRegex, "")
        .replace(/^(-.*?)-/g, "$1")
});

To enforce a leading dollar sign and no more than two decimals, and if the first character (after the $) is the decimal, prefix it with 0.

this.setState({
    value: "$" + event.target.value
        .replace(nonNumericRegex, "")
        .replace(/(\.\d\d)\d+/g, "$1")
        .replace(/^\./g, "0.")
})


回答2:

Remove the parseFloat and your string won't be cast to a number?

    change: function(event) {
        this.setState({value: event.target.value});
    }

http://jsfiddle.net/2hauj2qg/1/



回答3:

If it doesn't make sense to do anything with the number until they're done typing and you follow the standard way of raising an event to signal changed data, you can accomplish it with the following:

var MyComponent = React.createClass({
    getInitialState: function() {
        return {value: 4.5};
    },
    change: function(event) {
        this.setState({value: event.target.value});
    },
    blur: function(event) {
        this.props.onChange({value: parseFloat(event.target.value)});
    },
    render: function() {
        return <input type="number" value={this.state.value} onBlur={this.blur} onChange={this.change}/>;
    }
});

React.render(<MyComponent/>, document.body);

It doesn't make too much sense in this isolated example, but if you assume someone is using MyComponent and that they give it an onChange callback, then this works nicely. You get the benefits of a native input control but still return (through the callback) the number as an actual float.



回答4:

What about writing a small component that will handle string values and pass only legal float values to the listeners ?

class NumberInput extends React.Component<{ onChange: (n: number) => void, value: number }, { value: number }> {

  constructor(props: { onChange: ((n: number) => void); value: number }, context: any) {
    super(props, context);
    this.state = {
      value: props.value || 0
    };
  }

  handleInputChange = (e) => {
    const value = e.target.value;
    this.setState({
      value: value
    });
    if (this.props.onChange) {
      const floatValue = parseFloat(value);
      if (!isNaN(floatValue)) {
        this.props.onChange(floatValue)
      }
    }
  };

  componentWillReceiveProps(newProps) {
    this.setState({
      value: newProps.value
    })
  }

  render() {
    return (
      <Input step="0.1" value={this.state.value} onChange={this.handleInputChange} type="number"/>
    )
  }
}