最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - translating between cents and dollars in html input in React - Stack Overflow

programmeradmin0浏览0评论

I'm in a bit of a weird situation, I am dealing with currency in my we app. On the model side, I am saving currency as cents before sending to the server as I don't want to deal with decimal points on the server side. In the view however, I want the to display normal currency and not cents.

So, I have this input field where I take the data from dollars and change it to cents:

<input name="balance" type="number" step="0.01" min="0"
    placeholder="Balance in cents" onChange={this.handleUpdate}
    value={this.props.user.balance / 100} />

And when there's a change in the input value, I change it back to cents before sending it upstream:

handleUpdate: function(e) {
  
  var value = e.target.value;
  
  // changing it back from cents to dollars
  value = parseFloat(value) * 100;
  
  // save back to the parent component managing the prop
  this.props.onUserUpdate(value);
  
}

This puts me in kind of a deadlock, there's no way for me to enter a decimal point "." Let me demonstrate :

  1. 33 in the input box --> becomes 3300 in the parent state --> goes back as 33 in component prop - all good

  2. 33.3 in the input box --> becomes 3330 in the parent state --> goes back as 33.3 in the component prop - all good

  3. 33. in the input box --> becomes 3300 in the parent state --> goes back as 33 in the component prop - this is the problem

As you can see in case #3, when the user first enters "." this doesn't translate back to the same number with "."

Since it's a controlled input, there's basically no way of writing "."

I have tried using uncontrolled element with defaultValue, but the amount prop is not ready the time the component is rendered so it's just empty

/

I'm in a bit of a weird situation, I am dealing with currency in my we app. On the model side, I am saving currency as cents before sending to the server as I don't want to deal with decimal points on the server side. In the view however, I want the to display normal currency and not cents.

So, I have this input field where I take the data from dollars and change it to cents:

<input name="balance" type="number" step="0.01" min="0"
    placeholder="Balance in cents" onChange={this.handleUpdate}
    value={this.props.user.balance / 100} />

And when there's a change in the input value, I change it back to cents before sending it upstream:

handleUpdate: function(e) {
  
  var value = e.target.value;
  
  // changing it back from cents to dollars
  value = parseFloat(value) * 100;
  
  // save back to the parent component managing the prop
  this.props.onUserUpdate(value);
  
}

This puts me in kind of a deadlock, there's no way for me to enter a decimal point "." Let me demonstrate :

  1. 33 in the input box --> becomes 3300 in the parent state --> goes back as 33 in component prop - all good

  2. 33.3 in the input box --> becomes 3330 in the parent state --> goes back as 33.3 in the component prop - all good

  3. 33. in the input box --> becomes 3300 in the parent state --> goes back as 33 in the component prop - this is the problem

As you can see in case #3, when the user first enters "." this doesn't translate back to the same number with "."

Since it's a controlled input, there's basically no way of writing "."

I have tried using uncontrolled element with defaultValue, but the amount prop is not ready the time the component is rendered so it's just empty

http://jsfiddle.net/fpbhu1hs/

Share Improve this question edited Feb 2, 2022 at 17:28 A.L 10.5k10 gold badges71 silver badges105 bronze badges asked Jan 21, 2015 at 16:58 MichaelMichael 23k35 gold badges135 silver badges189 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 13

Controlled inputs using derived values can be tricksy - if you need to be able to display invalid or otherwise weird input then...

  1. always hold the input's value in its component's own state

    <input value={this.state.value} onChange={this.handleUpdate} // rest as above...
    
  2. derive the initial value in getInitialState()

    getInitialState: function() {
      return {value: this.props.user.balance / 100}
    }
    
  3. implement componentWillReceiveProps(nextProps) to detect when the prop's value is changing from above and re-derive the state value

    componentWillReceiveProps: function(nextProps) {
      if (this.props.user.balance != nextProps.user.balance) {
        this.setState({value: nextProps.user.balance / 100})
      }
    }
    

Now when the user enters "33.", you store their literal input using setState(), then call back to the parent.

handleUpdate: function(e) {
  var value = e.target.value
  this.setState({value: value})
  this.props.onUserUpdate(parseFloat(value) * 100)
}

If the value the parent then passes back down to the child via props hasn't changed (3300 == 3300 in this case), then componentWillReceiveProps() won't do anything.

Working snippet:

<script src="http://fb.me/react-with-addons-0.12.2.js"></script>
<script src="http://fb.me/JSXTransformer-0.12.2.js"></script>
<div id="example"></div>
<script type="text/jsx;harmony=true">void function() { 'use strict';

var Parent = React.createClass({
  getInitialState() {
    return {cents: 3300}
  },
  
  _changeValue() {
    this.setState({cents: Math.round(Math.random() * 2000 + Math.random() * 2000)})
  },

  _onCentsChange(cents) {
    this.setState({cents})
  },

  render() {
    return <div>
      <p><strong>Cents:</strong> {this.state.cents.toFixed(0)} <input type="button" onClick={this._changeValue} value="Change"/></p>
      <Child cents={this.state.cents} onCentsChange={this._onCentsChange}/>
    </div>
  }
})

var Child = React.createClass({
  getInitialState() {
    return {dollars: this.props.cents / 100}
  },

  componentWillReceiveProps(nextProps) {
    if (this.props.cents != nextProps.cents) {
      this.setState({dollars: nextProps.cents / 100})
    }
  },

  _onChange(e) {
    var dollars = e.target.value
    this.setState({dollars})
    if (!isNaN(parseFloat(dollars)) && isFinite(dollars)) {
      this.props.onCentsChange(parseFloat(dollars) * 100)
    }
  },

  render() {
    return <div>
      <input type="number" step="0.01" min="0" value={this.state.dollars} onChange={this._onChange}/>
    </div>
  }
})

React.render(<Parent/>, document.querySelector('#example'))

}()</script>

I'm using this simple solution to handle controlled inputs and decimal values.

  1. Create two props in your state, one to hold actual value and another to hold string.

    constructor(props) {
        ....
    
        this.state = {
            myProperty: 1.42,
            myPropertyString: '1.42'
        }
    }
    
  2. Set your input value to String one

    <input type="text"
           onChange={this.handleUpdate}
           value={this.state.myPropertyString}/>
    
  3. In handleUpdate method update both state variables.

    handleUpdate(e) {
        this.setState({
            myProperty: parseFloat(e.target.value),
            myPropertyString: e.target.value
        });
    }
    
发布评论

评论列表(0)

  1. 暂无评论