通常颠区,一些組件需要對同一個數據做出反應,這時我們建議將這些組件中關于這個數據的State提升至和它們距離最近的父級組件中。讓我看看該如何做到這一點:
在這個章節(jié)括袒,我們將會創(chuàng)建一個溫度計算器,根據用戶給定數值來計算水是否沸騰稿茉。
我們首先創(chuàng)建一個BoilingVerdict的組件锹锰,他接受celsius作為它的props,然后打印出水是否沸騰:
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>The water would boil.</p>;
}
return <p>The water would not boil.</p>;
}
接下來漓库,我們創(chuàng)建Calculator組件恃慧,它包含一個input控件,并將用戶輸入input控件的值保存在this.state.value中米苹,它依然包含上述中的BoilingVerdict組件糕伐,用來根據用戶輸入來反映水是否沸騰:
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {value: ''};
}
handleChange(e) {
this.setState({value: e.target.value});
}
render() {
const value = this.state.value;
return (
<fieldset>
<legend>Enter temperature in Celsius:</legend>
<input
value={value}
onChange={this.handleChange} />
<BoilingVerdict
celsius={parseFloat(value)} />
</fieldset>
);
}
}
在添加一個input
我們新的要求是這樣的,現在需要另一個Fahrenheit溫度的輸入框蘸嘶,它和上述Celsius的輸入框內的值保持同步良瞧。
首先我們可以將TemperatureInput組件從Calculator組件中抽象出來陪汽,我們添加一個scale的prop用來區(qū)分輸入框是Celsius還是Fahrenheit:
const scaleNames = {
c: 'Celsius',
f: 'Fahrenheit'
};
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {value: ''};
}
handleChange(e) {
this.setState({value: e.target.value});
}
render() {
const value = this.state.value;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={value}
onChange={this.handleChange} />
</fieldset>
);
}
}
現在我們的Calculator輸入框可以渲染兩個不同的輸入框了:
class Calculator extends React.Component {
render() {
return (
<div>
<TemperatureInput scale="c" />
<TemperatureInput scale="f" />
</div>
);
}
}
現在我們有兩個輸入框了,但是當我們給其中一個輸入框輸入內容時褥蚯,另一個輸入框的內容不會同步改變挚冤,這不符合我們我們的需求。由于當前的溫度值被隱藏在TemperatureInput組件的內部赞庶,所以Calculator組件無法知道當前的溫度值训挡,BoilingVerdict組件也就無法知道知道水是否沸騰。
State提升
首先我們需要兩個函數歧强,它們分別將fahrenheit轉為Celsius澜薄,將Celsius轉為ahrenheit:
function toCelsius(fahrenheit) {
return (fahrenheit - 32) * 5 / 9;
}
function toFahrenheit(celsius) {
return (celsius * 9 / 5) + 32;
}
上面的兩個函數都起到轉換數值的作用。我們在另外寫一個函數摊册,這個函數接受一個字符串和一個函數作為參數肤京,返回值為一個字符串。當輸入一個非數字的值時茅特,它返回一個空字符串:
function tryConvert(value, convert) {
const input = parseFloat(value);
if (Number.isNaN(input)) {
return '';
}
const output = convert(input);
const rounded = Math.round(output * 1000) / 1000;
return rounded.toString();
}
For example, tryConvert('abc', toCelsius) returns an empty string, and tryConvert('10.22', toFahrenheit) returns '50.396'.
Next, we will remove the state from TemperatureInput.
Instead, it will receive both value and the onChange handler by props:
class TemperatureInput extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
}
handleChange(e) {
this.props.onChange(e.target.value);
}
render() {
const value = this.props.value;
const scale = this.props.scale;
return (
<fieldset>
<legend>Enter temperature in {scaleNames[scale]}:</legend>
<input value={value}
onChange={this.handleChange} />
</fieldset>
);
}
}
如果一些組件需要用相同的state時忘分,只需要把這個state提升至最近的父組件內就好。在我們的例子中白修,這個組件便是Calculator妒峦,我們把value和scale儲存到它里面。
我們可以把兩個輸入框的值都儲存起來兵睛,但是這種做法沒有必要肯骇。我們只要儲存最近一次的數值和scale就可以了,因為我們可以根據value 和scale來計算另一個scale的value:
class Calculator extends React.Component {
constructor(props) {
super(props);
this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
this.state = {value: '', scale: 'c'};
}
handleCelsiusChange(value) {
this.setState({scale: 'c', value});
}
handleFahrenheitChange(value) {
this.setState({scale: 'f', value});
}
render() {
const scale = this.state.scale;
const value = this.state.value;
const celsius = scale === 'f' ? tryConvert(value, toCelsius) : value;
const fahrenheit = scale === 'c' ? tryConvert(value, toFahrenheit) : value;
return (
<div>
<TemperatureInput
scale="c"
value={celsius}
onChange={this.handleCelsiusChange} />
<TemperatureInput
scale="f"
value={fahrenheit}
onChange={this.handleFahrenheitChange} />
<BoilingVerdict
celsius={parseFloat(celsius)} />
</div>
);
}
}
現在卤恳,無論你在哪一個輸入框輸入數值累盗,Calculator組件中的this.state.value值和this.state.scale值都會被更新,這樣另一個沒被輸入數值的輸入框的值也會相應的被改變突琳。