使用 react 经常会遇到几个组件需要共用状态数据的情况。这种情况下,我们最好将这部分共享的状态提升至他们最近的父组件当中进行管理。我们来看一下具体如何操作吧
子组件执行受控handleChange 修改input输入 真正受控是在父组件的回调函数中进行的
们想要的是这两个输入能保持同步。当我们更新摄氏输入(Celsius)时,华氏度(Fahrenheit )这个框应该能显示转换后的的温度数值,反之亦然。
在React中,状态分享是通过将state数据提升至离需要这些数据的组件最近的父组件来完成的。这就是所谓的状态提升。我们会将 TemperatureInput
组件自身保存的 state 移到 Calculator
中。
如果 Calculator
组件拥有了提升上来共享的状态数据,那它就会成为两个温度输入组件的“数据源”。它会传递给下面温度输入组件一致的数据。由于两个 TemperatureInput
温度组件的props属性都是来源于共同的父组件 Calculator
,它们的数据也会保持同步。
首先,我们在 TemperatureInput
组件中将 this.state.temperature
替换为 this.props.temperature
。从现在开始,我们假定 this.props.temperature
属性已经存在了,不过之后仍然需要将数据从 Calculator
组件中传进去。
render() {
// 之前的代码: const temperature = this.state.temperature;
const temperature = this.props.temperature;
我们首先知道props是只读的 这么一个事实。而之前temperature
变量是被保存在其自身的 state 中的,TemperatureInput
组件只需要调用 this.setState()
就能改变它。但现在,temperature
是作为 prop 从父组件传递下来的,TemperatureInput
组件是没有控制权的。
在React中,这个问题通常是通过让组件“受控”来解决。就像 <input>
能够接受 value
和 onChange
这两个prop属性值,自定义组件 TemperatureInput
也能接受来自 Calculator
父组件的 temperature
变量和 onTemperatureChange
方法作为props属性值。
做完这些,当 TemperatureInput
组件更新它的温度数值时,就会调用 this.props.onTemperatureChange
方法。
handleChange(e) {
// 之前的代码: this.setState({temperature: e.target.value});
this.props.onTemperatureChange(e.target.value);
用父组件提供的 this.props.onTemperatureChange()
而不是this.setState()
方法:
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.onTemperatureChange(e.target.value);
}
render() {
const temperature = this.props.temperature;
const scale = this.props.scale;
return (
<fieldset>
<legend>在{scaleNames[scale]}:中输入温度数值</legend>
<input value={temperature}
onChange={this.handleChange} />
</fieldset>
);
}
}
父组件做受控的操作逻辑
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {temperature: '', scale: 'c'};
}
handleCelsiusChange(temperature) {
this.setState({scale: 'c', temperature});
}
handleFahrenheitChange(temperature) {
this.setState({scale: 'f', temperature});
}
render() {
const scale = this.state.scale;
const temperature = this.state.temperature;
const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;
const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
return (
<div>
<TemperatureInput
scale="c"
temperature={celsius}
onTemperatureChange={this.handleCelsiusChange} />
<TemperatureInput
scale="f"
temperature={fahrenheit}
onTemperatureChange={this.handleFahrenheitChange} />
<BoilingVerdict
celsius={parseFloat(celsius)} />
</div>
);
}
}