Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React.js: Avoid updates of currently edited input

Double-conversion problem

Demo: jsfiddle

/**
 * @jsx React.DOM
 */
var NumberField = React.createClass({
    render: function() {
        return <input type="number" value={this.props.value} onInput={this.onInput}/>;
    },
    onInput: function(e) {
        if (!this.props.onValueChange) {
            return;
        }
        var value = parseInt(e.target.value, 10);
        return this.props.onValueChange(value);
    }
});

var TempConverter = React.createClass({
    getInitialState: function() {
        return {c: 0};
    },
    render: function() {
        var c = this.state.c;
        return <p>
            <NumberField onValueChange={this.onChangeC} value={c}/> C
            <br/>
            <NumberField onValueChange={this.onChangeF} value={c2f(c)}/> F
        </p>;
    },
    onChangeC: function(c) {
        this.setState({c: c});
    },
    onChangeF: function(f) {
        this.setState({c: f2c(f)});
    }
});


function c2f(c) {
    return 1.8 * c + 32;
}
function f2c(f) {
    return 0.5555555555555 * (f - 32);
}

When I change the °F value by one, it gets converted to Celsius and then back to Fahrenheit, resulting in rounding errors.

Is there any way to avoid updates on the currently modified element?

punch back

Backbone.js has the exact same problem.

like image 989
NVI Avatar asked Dec 27 '13 06:12

NVI


1 Answers

You ask,

Is there any way to avoid updates on the currently modified element?

In general, not really. This is actually a feature of React: your UI is guaranteed to always be in sync with your underlying data! Of course, by overriding shouldComponentUpdate and doing some manual work you can prevent the fields from changing, but it's intentional that the path of least resistance leads you to an always-accurate UI.

It sounds like you want to be able to show either an exact °C temperature or an exact °F temperature, so the best approach here seems to be to store that as well. Otherwise, you're feigning more precision than you actually have. I've modified your code so that it operates in one of two modes: Celsius mode or Fahrenheit mode:

var TempConverter = React.createClass({
    getInitialState: function() {
        return {type: "c", val: 0};
    },
    render: function() {
        var c, f;
        if (this.state.type === "c") {
            c = this.state.val;
            f = c2f(c);
        } else {
            f = this.state.val;
            c = f2c(f);
        }

Tracking the value this way allows you to store and display the exact temperature the user entered and be confident that you're not losing any precision.

Live demo here: jsfiddle

(You could also round the temperature before displaying, of course, but your response to your Backbone.js question indicates that's not your preferred solution.)

like image 88
Sophie Alpert Avatar answered Oct 05 '22 21:10

Sophie Alpert