在 react 中有個特性稱為 單向資料流 unidirectional flow
簡單說就是 child component 內的資料都來自於 parent component 中的同個 state
在 parent component 中把 state 改變為 props 傳給 child component
達成 所有 child component 的內容都同步於 parent component,parent component 的 state 是唯一真實資料來源
實做一個 溫度換算器 來解釋這個概念
接收 temperature 後提升 state 到共同的 ancestor component,在 ancestor component 內做運算
再 pass down 回 child component
從輸入數值後發生的一連串過程如下
首先 寫 BoilingVerdict
這個 stateless component 接收 名為 celsius 的 props,顯示出 水是否沸騰
function BoilingVerdict(props) {
if (props.celsius >= 100) {
return <p>水溫已達沸點</p>;
}
return <p>水溫未達沸點</p>;
}
接下來寫上層的 Calculator component
Calculator 渲染出 一個 <input /> 輸入 temperature 並將值存在 local state 裡
另外將 temperature 作為 props 傳到 BoilingVerdict 裡
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>
);
}
}
這階段對 input 輸入 顯示出水溫是否已達沸點的資訊
將 Calculator component 拆成 兩個 TemperatureInput 分別顯示出華氏及攝氏的值
import React from 'react';
const scaleNames={
c:'Celsius',
f:'Fahrenheit'
};
class TempInput extends React.Component{
constructor (props){
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
temperture:''
}
}
//
handleChange(e){
this.setState({temperature : e.target.value});
}
render(){
const {temperature} = this.state;
const {scale} = this.props;
return(
<div>
<p>輸入 {scaleNames[scale]} 溫度</p>
<input
value={temperature}
onChange={this.handleChange} />
</div>
)
}
}
export default TempInput;
Calculator 改寫為
...
return(
<div>
<TempInput scale="c" />
<TempInput scale="f" />
<BoilComp celsius={temperature}/>
</div>
)
...
但這時候兩個 component 的 資料沒有共享,所以輸入其中一個欄位 另一個並不會更新
在 Calcuator 中增加 兩種度數換算的函式 toCelsius / toFahrenheit
// 去掉非數字值,去掉小數點及進位
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();
}
// 換算為攝氏
function toCelsius(fahrenheit) {
return ((fahrenheit - 32) * 5) / 9;
}
// 換算為華氏
function toFahrenheit(celsius) {
return (celsius * 9) / 5 + 32;
}