我一直在通过编写一个小型计算器应用程序来学习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 :(得分: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} />
答案 1 :(得分: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
的基本思路是使用其他变量来存储业务逻辑状态,并仅为UI状态保留this.state
。
PS。真正的计算器具有比这更复杂的业务逻辑。您应该在规范中明确定义每个状态和状态机。