前面幾篇文章把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
,不妨就把它拿出來到共同的父組件吧!
結果如下:
最後如果上方文章中有任何錯誤或解釋不清楚的地方,再麻煩各位大大留言告訴我,小弟會再盡快修正!謝謝大家
參考文章: