反应:输入范围滑块未将最后一个值传播给 Chrome 中的所有者
React: Input range slider not propagating last value to its owner in Chrome
以下fiddle使用React 0.13将3个输入范围显示为受控组件。状态更改后,彩色方块会更新以显示新的 rgb 颜色。 http://jsfiddle.net/faria/yw6q3s9L/
它在 Firefox 中的工作方式与我 expect/desire 相同,但在 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'));
您在 ColorSlider
中的 handleOnChange
中存在竞争条件。您应该在状态更新后调用 this.props.changeHandler()
,而不是在调用 setState
之后直接调用。这是因为 setState
不是同步的。然而,它可能在 Firefox 中正常工作的原因是因为它可能是一场非常快的比赛,而 Firefox 恰好出现在没有显示错误的一侧。
要强制它始终工作并消除竞争,请在 ColorSlider
中对 handleOnChange
使用类似的东西:
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]);
})
}
允许您通过将 update
函数更改为类似以下内容来设置父 App
组件中的颜色变化:
update: function(name, value) {
var stateChange = {};
stateChange[name] = value;
this.setState(stateChange);
}
以下fiddle使用React 0.13将3个输入范围显示为受控组件。状态更改后,彩色方块会更新以显示新的 rgb 颜色。 http://jsfiddle.net/faria/yw6q3s9L/
它在 Firefox 中的工作方式与我 expect/desire 相同,但在 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'));
您在 ColorSlider
中的 handleOnChange
中存在竞争条件。您应该在状态更新后调用 this.props.changeHandler()
,而不是在调用 setState
之后直接调用。这是因为 setState
不是同步的。然而,它可能在 Firefox 中正常工作的原因是因为它可能是一场非常快的比赛,而 Firefox 恰好出现在没有显示错误的一侧。
要强制它始终工作并消除竞争,请在 ColorSlider
中对 handleOnChange
使用类似的东西:
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]);
})
}
允许您通过将 update
函数更改为类似以下内容来设置父 App
组件中的颜色变化:
update: function(name, value) {
var stateChange = {};
stateChange[name] = value;
this.setState(stateChange);
}