前面幾篇文章把React基本的用法都說完了,剩下的就是該如何將它應用在實務面中,所以接下來我會試著和各位一同做出一些小作品,但是在那之前,我們得先了解如何正確的使用React開發網站是最好的!當然小弟也還是新手,所以如果接下來的文章中有任何不適當的地方,還麻煩請各位大大留言告訴我了!謝謝!
沒錯!要記得提升狀態!再解釋這個觀念前,先說說狀態吧!
之前大多時候都是用英文,所以看中文可能反而不認識他XD,哎!但是套一句某牌奶茶的原來我們這麼近,這個「狀態」就是我們常用的state!
那要提升狀態是什麼?就是盡量把可共用的state往外面的組件放,例如說有A和B組件同時都需要一個state的值,那我們就可以把這個state放在離A和B最近的一個父組件上,例如官方給了溫度計的例子。
上面提到了官方文件上的例子:溫度計,以下讓我們來實做看看:
//判斷溫度是否達沸點
class Title extends React.Component {
render(){
//溫度100度以上就到達沸點
return <h1>{(this.props.temperature>=100 ? '達到沸點!!!':'未到沸點...')}</h1>
}
}
class Celsius extends React.Component {
constructor(props) {
super(props)
//用state來記錄溫度數值
this.state = ({ temperature : 0 })
//設定changeState是在此class下執行
this.changeState = this.changeState.bind(this)
}
changeState(event){
//取得目前輸入的值
let temperature = event.target.value
//把值寫到state裡面
this.setState({ [event.target.name] : temperature })
}
render() {
return(
/*用Title組件顯示目前輸入的溫度*/
<div>
<Title temperature={this.state.temperature} />
{/*這裡顯示輸入的溫度*/}
<span>目前攝氏溫度是:{this.state.temperature}</span><br/>
{/*輸入溫度的地方*/}
<input name="temperature"
value={this.state.temperature}
onChange={this.changeState}/>
</div>
)
}
}
ReactDOM.render(<Celsius />, document.getElementById('root'))
結果會如下,在組件中使用state儲存溫度,並判斷他如果超過100度就達到沸點:
目前乍看之下感覺也沒什麼,只是用之前學到的東西做出一個簡單的例子,但是這也只是目前而已,當我們再進階一點,將上方的例子再加入華氏的溫度輸入,那該怎麼做呢?首先讓我們保持著組件「Don't repeat yourself」的概念,試著改寫Celsius讓他可以透過傳入props來重複使用在兩種溫度單位上。
這邊Title的部分就先不說,其他就像上面說的,先將輸入溫度的Input組件先做出來,之後再建立另一個EasyForm組件當作他的父組件並在render使用它,以下會把EasyForm和Input一起說明:
//輸入溫度的組件
class InputTemperature extends React.Component{
/*因為將該組件需要用到的state(例如:state.temperature或state.changeState)都移到共同的父組件<EasyForm>
所以這裡只需要render負責輸出組件內容*/
render(){
return(
/*所有的資料都從父組件傳進來,所以使用props接收資料,包括function*/
<div>
<span>目前輸入溫度是:{this.props.temperature}度{this.props.type}</span><br/>
<input name="temperature"
value={this.props.temperature}
onChange={this.props.changeState} />度{this.props.type}
</div>
)
}
}
class EasyForm extends React.Component {
constructor(props) {
super(props)
//用state來記錄溫度數值和該數值是哪個溫度單位(攝氏或華氏)
this.state = ({ temperature : 0,type : '' })
//設定changeState是在此class下執行
this.changeState = this.changeState.bind(this)
}
//轉換溫度單位
toConvert(temperature,type){
//如果type是C就帶公式將華氏轉攝氏,F就轉華氏
if (type == 'C')
return (temperature-32)*5/9
else
return (temperature*9/5)+32
}
//傳入type代表現在是哪種溫度變化
changeState(type){
//取得目前輸入的值
let temperature = window.event.target.value
//將目前溫度和溫度的單位寫進去state中
this.setState({ 'temperature' : temperature,'type' : type })
}
render() {
/*在render的時候,先去取state判斷目前儲存的溫度是攝氏還華氏
華氏的話temperature_F就不用轉,攝氏的話換temperature_C不轉
但是如果有不同就得傳進toConvert中做單位的轉換*/
let temperature_C = this.state.type=='F' ? this.toConvert(this.state.temperature,'C') : this.state.temperature
let temperature_F = this.state.type=='C' ? this.toConvert(this.state.temperature,'F') : this.state.temperature
return(
<div>
{/*因為條件設定設是大於100度,所以傳入攝氏溫度*/}
<Title temperature={temperature_C} />
{/*同樣都是<InputTemperature />,
但根據設置的props不同,就會有不同的結果*/}
<InputTemperature type="C"
temperature={temperature_C}
/*設定事件changeState,
所以在<InputTemperature />中就可以用props呼叫該事件*/
changeState={this.changeState.bind(this,'C')} />
<InputTemperature type="F"
temperature={temperature_F}
changeState={this.changeState.bind(this,'F')} />
</div>
)
}
}
嗚嗚,我一直很想講的是JSX的程式碼區塊真的不太容易閱讀,還請各位大大包含![]()
上方的程式碼已經有註解了,但下方還是說明一下程式撰寫的過程:
建立一個InputTemperature的class組件,並在render回傳:
<div>
<span>目前輸入溫度是:{this.props.temperature}度{this.props.type}</span>
<br/>
<input name="temperature"
value={this.props.temperature}
onChange={this.props.changeState} />度{this.props.type}
</div>
雖然InputTemperature內只有render但是input內有不少用props傳進來的資料,像是控制值的value={this.props.temperature}和觸發事件會執行的onChange={this.props.changeState},這些資料從哪裡來呢?讓我們繼續看下去!
從上方程式碼中還有一個EasyForm,先來說說他的組成,首先是constructor中的state設定了this.state = ({ temperature : 0,type : '' }),temperature用來儲存溫度另一個this.changeState = this.changeState.bind(this)用來設定溫度變化時執行的function。
接著來看看changeState的內容:
changeState(type){
//取得目前輸入的值
let temperature = window.event.target.value
//將目前溫度和溫度的單位寫進去state中
this.setState({ 'temperature' : temperature,'type' : type })
}
可以看到傳入的參數type,這裡會寫進溫度temperature和溫度單位type(攝氏或華氏),並更新到state中,而這個function也透過props傳入InputTemperature的onChange中。
眼尖的大大應該也有發現EasyForm中還有一個用來換算單位的function:「toConvert」
toConvert(temperature,type){
//如果type是C就帶公式將華氏轉攝氏,F就轉華氏
if (type == 'C')
return (temperature-32)*5/9
else
return (temperature*9/5)+32
}
上方的function用來轉換溫度用的,我們在EasyForm的render中會用到它。
EasyForm的render,以下就是重點了,所以我們分兩段來看,一個是return的部份、另一個是reutrn之前,我們先看return之前做了哪些事情:
/*在render的時候,先去取state判斷目前儲存的溫度是攝氏還華氏*/
let temperature_C = this.state.type=='F' ? this.toConvert(this.state.temperature,'C') : this.state.temperature
let temperature_F = this.state.type=='C' ? this.toConvert(this.state.temperature,'F') : this.state.temperature
這裡要考驗大家的記憶力了!記得我們在InputTemperature改變的時候發生什麼事嗎?沒錯!會透過onChange觸發從props傳入的changeState,然後changeState改變了溫度和溫度單位,所以這時候儲存的溫度單位就是最近一次輸入的溫度是攝氏或華氏,那為什麼還要在這邊放兩個變數去裝?不就把state的temperature傳回去不就好了嗎?
因為我們這時候分成攝氏和華氏,如果我在華氏的input輸入新溫度,那攝氏的input就要跟著變動,所以如果我在某一個input輸入溫度那這裡就會判斷state中的type是攝氏還華氏(這裡攝氏用C儲存、華氏用F),所以用來放攝氏溫度的temperature_C變數會先去判斷type是不是攝氏,如果是攝氏就直接帶入state中的temperature,但是如果是華氏就要先經過toConvert轉換成攝氏後再給temperature_C,而華氏temperature_F的部分就是反過來。
總算是最後一步了,算是把所有放出去的線都收回來,讓我們看看EasyForm的render他return了什麼:
<div>
<Title temperature={temperature_C} />
{/*同樣都是<InputTemperature />,
但根據設置的props不同,就會有不同的結果*/}
<InputTemperature type="C"
temperature={temperature_C}
/*設定事件changeState,
所以在<InputTemperature />中就可以用props呼叫該事件*/
changeState={this.changeState.bind(this,'C')} />
<InputTemperature type="F"
temperature={temperature_F}
changeState={this.changeState.bind(this,'F')} />
</div>
Title的部份因為我們是用攝氏判斷沸點,所以要傳轉換成攝氏的temperature_C給他。
InputTemperature的部分傳入了三個值,第一個是type用來渲染單位,第二個是把溫度各自轉換後的值temperature,最後是設定onChange觸發後執行的function,function後帶有.bind(this,'C'),之前文章有提過觸發事件傳入參數的方法,所以後面的'C'會傳入到changeState(type)的type中更新state。
以上!就是撰寫的過程了!我們把原本第一個範例中輸入溫度的Celsius組件中的state都放到外面的EasyForm,這樣紀錄temperature溫度的值就是單向的,不會分散在兩個組件(就算是同一組件用兩次也是)中的state紀錄,所以以後有儲存相同資料的state,不妨就把它拿出來到共同的父組件吧!
結果如下:
最後如果上方文章中有任何錯誤或解釋不清楚的地方,再麻煩各位大大留言告訴我,小弟會再盡快修正!謝謝大家![]()
參考文章: