2014-09-23 82 views
1

我一直在通過編寫一個小型計算器應用程序來學習React.js。我認爲事情進展得很順利,直到我知道setState是異步的,因此我的突變不能立即應用。React.js瞭解setState

所以我的問題是,基於值被添加到輸入保持運行總量的最佳方式是什麼。以下例子:

var Calculator = React.createClass({ 
    total : 0, 

    getInitialState : function(){ 
    return { 
     value : '0' 
    }; 
    }, 

    onValueClicked : function (value) { 
    var actual, total, current = this.state.value; 

    if(value === '+') { 
     actual = this.total = parseInt(this.total, 10) + parseInt(current, 10); 
    } else { 
     if(parseInt(current, 10) === 0) { 
     actual = value; 
     } else { 
     actual = current.toString() + value; 
     } 
    } 

    this.setState({ value : actual }); 
    }, 

    render : function() { 
    return (
     <div className="calc-main"> 
     <CalcDisplay value={this.state.value} /> 
     <CalcButtonGroup range="0-10" onClick={this.onValueClicked} /> 
     <CalcOpButton type="+" onClick={this.onValueClicked} /> 
     </div> 
    ) 
    } 
}); 

var CalcDisplay = React.createClass({ 
    render : function() { 
    return (
     <input type="text" name="display" value={this.props.value} /> 
    ); 
    } 
}); 

var CalcButtonGroup = React.createClass({ 
    render : function() { 
    var i, buttons = [], range = this.props.range.split('-'); 

    for(i = range[0]; i < range[1]; i++) { 
     var handler = this.props.onClick.bind(null, i); 

     buttons.push(<CalcNumberButton key={i} onClick={ handler } />); 
    } 

    return (
     <div className="calc-btn-group">{ buttons }</div> 
    ); 
    } 
}); 

var CalcNumberButton = React.createClass({ 
    render : function() { 
    return (
     <button onClick={this.props.onClick}>{this.props.key}</button> 
    ); 
    } 
}); 

var CalcOpButton = React.createClass({ 
    render : function() { 
    var handler, op = this.props.type; 

    handler = this.props.onClick.bind(null, op); 

    return (
     <button onClick={handler}>{op}</button> 
    ); 
    } 
}); 

React.renderComponent(<Calculator />, document.getElementById('container')); 

在上面的例子中,我完全放棄了將狀態存儲在狀態中並將其保留在外部。我讀過你可以在setState完成時進行回調運行,但在計算器的情況下,我需要它快速更新並快速更新。如果狀態沒有得到每個按鈕按下更新,我迅速按下按鈕 - 事情將失去同步。是我所錯過的回調,還是我完全錯誤地思考這個問題?

任何幫助表示讚賞!

+0

說實話,如果你不需要在'render'函數中使用'this.total',那麼保持它在狀態之外就可以了。只有在對「總是」進行重新渲染的情況下,東西才需要處於狀態。如果在更改this.total時實際上並不需要總是重新渲染,那麼您可以保持原樣。 – 2014-09-23 14:04:36

+0

如果我沒有弄錯,在當前的實現中,如果調用'setState'來響應一個事件,狀態將在任何進一步事件分派之前解決。 – 2014-09-23 23:57:17

+0

我認爲這裏問題的一部分是我是console.log'狀態值。但是,我發現如果我試圖在狀態中存儲多個項目,並在一個地方和另一個地方更改了一個字段,則這兩個值將不同步。即如果我將操作數存儲在狀態中並在下一次交互中檢查它的存在 - 它不會始終設置。 – backdesk 2014-09-24 09:03:10

回答

2

它是異步的,但比最快的人類點擊速度快得多。


除此之外,您應該在componentDidMount中聲明實例變量,例如,

componentDidMount: function(){ 
    this.total = 0; 
} 

...但在這種情況下,您可能想要將其存儲在狀態。


.split返回一個字符串數組,你要使用的數字:共

range = this.props.range.split('-').map(Number) 

或避免串(者優先)的其中之一:

<CalcButtonGroup range={[0, 10]} onClick={this.onValueClicked} /> 
<CalcButtonGroup range={{from: 0, till: 10}} onClick={this.onValueClicked} /> 
+0

我不同意*,但在這種情況下,您可能希望將其存儲在狀態*中。'state'只用於UI狀態** only **,另一個(subscribe)變量用於邏輯狀態。 – 2015-07-08 03:49:24

+0

你需要用數據來產生狀態+道具的渲染輸出。因此,您可以存儲總計或操作順序,或者您想要表示的順序。但是,如果它影響渲染效果並且不是道具或狀態,則會很快遇到問題,並且事情不會同步。 – FakeRainBrigand 2015-07-08 04:03:03

+0

查看我的帖子。此外,解決*如果影響渲染而不是道具或狀態*的另一個想法是在該變量上訂閱'this.state'。而且,只有那個變量可以更新'this.state'。像這樣流動:'data' - > calculation - >'data' - >'this.state'。 – 2015-07-08 04:20:17

0

你已經爲您的業務邏輯狀態定義了total變量。爲什麼不儲存更多這樣的信息?

var Calculator = React.createClass({ 
    previous: 0, // <-- previous result 
    current: 0, // <-- current display 
    op: '', // <-- pending operator 

    getInitialState : function(){ 
    return { 
     value : '0' 
    }; 
    }, 

    onValueClicked : function (value) { 
    var actual; 

    if(value === '+') { 
     this.previous = this.current; 
     this.op = '+'; 
     actual = 0; // start a new number 
    } else if (value === '=') { 
     if (this.op === '+') { 
     actual = this.previous + this.current; 
     } else { 
     actual = this.current; // no-op 
     } 
    } else { 
     actual = current * 10 + value; 
    } 

    this.current = actual; // <-- business logic state update is synchronous 
    this.setState({ value : String(actual) }); // <-- this.state is only for UI state, asynchronous just fine 
    }, 

    render : function() { 
    return (
     <div className="calc-main"> 
     <CalcDisplay value={this.state.value} /> 
     <CalcButtonGroup range="0-10" onClick={this.onValueClicked} /> 
     <CalcOpButton type="+" onClick={this.onValueClicked} /> 
     <CalcOpButton type="=" onClick={this.onValueClicked} /> 
     </div> 
    ) 
    } 
}); 

的基本思路,以解決this.state是使用其他變量來存儲你的業務邏輯狀態,並保留this.state只對UI狀態。

PS。真實的計算器比這更復雜的業務邏輯。你應該明確規定每個狀態和狀態機。

+0

如果要將業務邏輯與呈現狀態分開,請將業務邏輯移至容器組件,而不是組件上的實例屬性。 – 2017-03-19 03:05:47