以下小提琴使用React 0.13将3个输入范围显示为受控组件。状态改变后,彩色方块更新以显示新的rgb颜色。 http://jsfiddle.net/faria/yw6q3s9L/
它可以像我在Firefox中所期望的那样工作,但不能在Chrome中工作。具体来说,彩色方块组件显示倒数第二个值,而滑块显示最新值。
这是React / Chrome错误吗?或者我可以在我的代码中修复一些内容吗?
代码:
var ColorDisplay = React.createClass({
propTypes: {
red: React.PropTypes.number.isRequired,
green: React.PropTypes.number.isRequired,
blue: React.PropTypes.number.isRequired
},
_toHex: function(s) {
var hex = parseInt(s,10).toString(16);
return hex.length == 1 ? "0" + hex : hex;
},
render: function() {
var rr = this._toHex(this.props.red),
gg = this._toHex(this.props.green),
bb = this._toHex(this.props.blue);
var color = "#" + rr + gg + bb;
var divStyle = {backgroundColor: color};
return (
<div id="display">
<h3>{color}</h3>
<div id="color-swatch" style={divStyle}></div>
</div>
);
}
});
var ColorSlider = React.createClass({
propTypes: {
red: React.PropTypes.number.isRequired,
green: React.PropTypes.number.isRequired,
blue: React.PropTypes.number.isRequired,
changeHandler: React.PropTypes.func.isRequired
},
getInitialState: function() {
return {
red: this.props.red,
green: this.props.green,
blue: this.props.blue
};
},
handleOnChange: function(name, event) {
var new_state = {};
new_state[name] = event.target.value;
this.setState(new_state);
this.props.changeHandler();
},
handleClick: function(name, event) {
var new_state = {};
var self = this;
['red','green','blue'].forEach(function(name) {
new_state[name] = React.findDOMNode(self.refs[name]).value;
});
this.setState(new_state);
this.props.changeHandler();
},
_getRGB: function() {
return this.state;
},
_toHex: function(s) {
var hex = parseInt(s,10).toString(16);
return hex.length == 1 ? "0" + hex : hex;
},
render: function() {
var red_style = {color: 'red'};
var grn_style = {color: 'green'};
var blue_style = {color: 'blue'};
var min=0;
var max=255;
var step=1;
var rr = this._toHex(this.state.red),
gg = this._toHex(this.state.green),
bb = this._toHex(this.state.blue);
return (
<div id="slider">
<div>
<label htmlFor="id_red">Red</label>
<input
name="red"
type="range"
min={min}
max={max}
step={step}
value={this.state.red}
onInput={this.handleOnChange.bind(this, "red")}
onChange={this.handleOnChange.bind(this, "red")}
ref="red"
id="id_red"
/>
<span style={red_style}>{this.state.red} / {rr}</span>
</div>
<div>
<label htmlFor="id_green">Green</label>
<input
name="green"
type="range"
min={min}
max={max}
step={step}
value={this.state.green}
onInput={this.handleOnChange.bind(this, "green")}
onChange={this.handleOnChange.bind(this, "green")}
ref="green"
id="id_green"
/>
<span style={grn_style}>{this.state.green} / {gg}</span>
</div>
<div>
<label htmlFor="id_blue">Blue</label>
<input
name="blue"
type="range"
min={min}
max={max}
step={step}
value={this.state.blue}
onInput={this.handleOnChange.bind(this, "blue")}
onChange={this.handleOnChange.bind(this, "blue")}
ref="blue"
id="id_blue"
/>
<span style={blue_style}>{this.state.blue} / {bb}</span>
</div>
<button onClick={this.handleClick}>Set Color (hack to sync slider with square on Chrome)</button>
</div>
);
}
});
var App = React.createClass({
getInitialState: function() {
return { red: 0, green: 0, blue: 0 };
},
update: function() {
var rgb = this.refs.slider._getRGB();
// the values are now strings!
this.setState({
red: rgb.red,
green: rgb.green,
blue: rgb.blue
});
},
render: function() {
return(
<div id="color-app">
<ColorDisplay ref="color"
red={this.state.red}
green={this.state.green}
blue={this.state.blue}
/>
<ColorSlider ref="slider"
red={this.state.red}
green={this.state.green}
blue={this.state.blue}
changeHandler={this.update}
/>
</div>
);
}
});
React.render(<App />, document.getElementById('app'));
答案 0 :(得分:1)
handleOnChange
ColorSlider
中存在竞争条件。一旦在调用this.props.changeHandler()
后没有直接更新状态,您应该调用setState
。这是因为setState
不是同步的。然而,它可能正确地在Firefox中运行的原因是因为它可能是一个非常快速的竞争,并且firefox恰好出现在没有显示错误的一面。
要强制它始终有效并消除竞争,请在handleOnChange
中对ColorSlider
使用类似的内容:
handleOnChange: function(name, event) {
var new_state = {};
new_state[name] = event.target.value;
this.setState(new_state, function() {
this.props.changeHandler();
});
}
它会正常工作。这样做是为了确保仅在状态实际更新并呈现后才调用this.props.changeHandler
。由于这个特定原因,setState
有一个可选的回调。
请注意,您不必将setState
中的回调绑定到this
,因为React会为您自动绑定它。
进一步修改:
您也只会发生此问题,因为您直接从父级(坏)调用子组件功能,并且您在不需要时直接从DOM读取数据。从外部调用组件的方法在React中是一个巨大的禁忌。你应该做的是通过onChange回调将新颜色值传递给链,然后在父进程中以这种方式对它做出反应。
EG:
handleOnChange: function(name, event) {
var new_state = {};
new_state[name] = event.target.value;
this.setState(new_state, function() {
this.props.changeHandler(name, this.state[name]);
})
}
允许您通过将App
功能更改为以下内容来设置父update
组件中的颜色更改:
update: function(name, value) {
var stateChange = {};
stateChange[name] = value;
this.setState(stateChange);
}