- 多个组件需要反映相同的变化数据,建议将共享状态提升到最近的共同父组件中
1 创建一个用于计算水在给定温度下是否会沸腾的温度计算器
1.1 组件 BoilingVerdict
接收 celsius
温度作为一个 porp
function BoilingVerdict(props) {
if(props.celsius >= 100) {
return <p>The water would boil.</p>
}
return <p>The water would not boil.</p>
}
class Calculator extends React.Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.state = { temperature: '' }
}
handleChange(e) {
this.setState({
temperature: e.target.value
})
}
render() {
const temperature = this.state.temperature
return (
<fieldset>
<legend>Enter temperature in Celsius:</legend>
<input value={ temperature } onChange={ this.handleChange } />
<BoilingVerdict celsius={ parseFloat(temperature) } />
</fieldset>
)
}
}
2 提供华氏度输入框,保持与摄氏度输入框数据同步
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
}
class TemperatureInput extends React.Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.state = { temperature: '' }
}
handleChange(e) {
this.setState({
temperature: e.target.value
})
}
render() {
const temperature = this.state.temperature
const scale = this.props.scale
return (
<fieldset>
<legend>Enter temperature in { scaleNames[scale] }:</legend>
<input value={ temperature } onChange={ this.handleChange } />
</fieldset>
)
}
}
2.2 修改Calculator
组件,使其渲染两个独立的温度输入框组件
class Calculator extends React.Component {
render() {
<div>
<TemperatureInput scale="c" />
<TemperatureInput scale="f" />
</div>
}
}
此时在其中一个输入框输入温度,另一个并不会更新
- 因为 Calculator 组件并不知道在 TemperatureInput 组件中的当前温度是多少,所以通过 Calculator 组件并不能展示 BoilingVerdict 组件的渲染结果
2.3 编写两个可以在华氏度与摄氏度之间相互转换的函数
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9
}
function toFahrenheit(celsius) {
return celsius * 9 / 5 + 32
}
- 当输入
temperature
的值无效时,函数返回空字符串
function tryConvert(temperature, convert) {
const input = parseFloat(temperature)
if(Number.isNaN(input)) return ''
const output = convert(input)
const rounded = Math.round(output * 1000) / 1000
return rounded.toString()
}
2.4 编写函数将两个输入框的值做关联
状态提升:将多个组件中需要共享的 state
向上移动到它们的最近共同父组件中,便可实现共享 state
render() {
// before: const temperature = this.state.temperature
const temperature = this.props.temperature
// ...
}
handleChange(e) {
// before: this.setState({ state: e.target.vaule })
this.props.onTemperatureChange(e.target.vaule)
}
class TemperatureInput extends React.Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
}
handleChange(e) {
this.props.onTemperatureChange(e.target.vaule)
}
render() {
const temperature = this.props.temperature
const scale = this.props.scale
return (
<fieldset>
<legend>Enter temperature in { scaleNames[scale] }:</legend>
<input value={ temperature } onChange={ this.handleChange } />
</fieldset>
)
}
}
父组件需要调用 Calculator
的处理
- 在摄氏度输入框中键入 37 时,
Calculator
组件中的 state
将会是
{
temperature: '37',
scale: 'c'
}
- 在华氏度的输入框中键入 212 时,
Calculator
组件中的state
将会是
{
temperature: '212',
scale: 'f'
}
① 存储最近修改的温度及其计量单位,根据当前的 temperature
和 scale
就可以计算出另一个输入框的值
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({
temperature,
scale: 'c'
})
}
handleFahrenheitChange(temperature) {
this.setState({
temperature,
scale: 'f'
})
}
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>
<TemperatureUnput
scale="c"
temperature={ celsius }
onTemperatureChange={ this.handleCelsiusChange }
>
<TemperatureUnput
scale="f"
temperature={ fahrenheit }
onTemperatureChange={ this.handleFahrenheitChange }
>
<BoilingVerdict celsius={ parseFloat(celsius) } />
</div>
)
}
}
3 总结
- 在 React 应用中,任何可变数据应当只有一个相对应的唯一“数据源”。
state
都是首先添加到需要渲染数据的组件中去。
- 然后,如果其他组件也需要这个
state
,那么可以将它提升至这些组件的最近共同父组件中。
应当依靠自上而下
的数据流,而不是尝试在不同组件间同步 state