皮卡丘就這麼跑進我的文章裡。。。
目前我的專案長這樣~
我一樣用電子名片這個專案來學習 React props 的運作原理,今天也會將我預先儲存在 json 裡的資料(假裝它是從 API 或是後端抓來的資料)更新到我的電子名片上。
props 讓我們可以從 parent component 傳內容到 child component 中,props 讓 component 之間可以互相溝通,靈活地傳遞資訊。當一個 component 有所更動,它的狀態(States)也會有所改變,而 child component 會自動更新透過 props 去接受 parent component 裡面被更新的值。
我們現在的 parent component 裡面的內容
class Card extends React.Component{
render (){
return(
<div className="card">
<Photo />
<Bio />
<Updates />
</div>
)
}
}
還記得,我們是在這個最主要的 Card 類別組件裡面,把整個我們之前拆解的 sub components 重新整合回來放在這邊。
我們可以透過 props
在這個 components 裡面,給予新的內容。
舉個例子,假設現在我要做出一隻皮卡丘,於是我們有定義顏色的 Color component
,告訴機器我要黃色的皮卡丘;也有定義表情的 Expressive component
與設定它的動作的 Action component
,不過這個階段還不確定我要讓皮卡丘有什麼表情跟動作,我可不可以在最後製作前再告訴機器我要什麼樣的皮卡丘,答案是可以的。
Pikachu component
作為 parent component
,它就像是製作的機器,裡面接收了所有 sub components 所定義的資訊,目前的資訊是:
做出一隻 黃色皮卡丘
可能長這樣:(最普通皮卡丘xD)
但在按下機器,製作皮卡丘之前,我想要讓他看起來受到驚嚇然後雙手很僵硬。我們可以在 Pikachu component
裡面直接賦予 <Expressive />
和 <Action />
你想要設定的值,而 child components 會自動抓取這個在 parent component 裡新增的值。我們只需要在<Expressive />
裡面設定「驚訝的表情」;動作的部分在 <Action />
裡設定「僵硬」。
最後,機器就會幫我們做出 受到驚嚇,雙手僵硬的黃色皮卡丘
like this
回到我們現有的專案,我們可以在 Card component
裡面,設定在 Bio component
裡面已經定義好的 name
和 location
。
作法如下
class Card extends React.Component{
render (){
return(
<div className="card">
<Photo />
<Bio name={"Annie Tsai"} location={"New York"}/>
<Updates />
</div>
)
}
並且在 Bio
的類別屬性裡,呼叫我們在 parent component 裡面設定的值。
呼叫的方式:
{this.props.<className>}
所以我們在 className="name"
和 className="location"
裡面,加上呼叫 props 的程式。
class Bio extends React.Component{
render (){
return(
<div className="bio">
<h1 className="name">{this.props.name}</h1>
<h2 className="location">{this.props.location}</h2>
<div className="occupation">
<p>Front End Developer</p>
</div>
</div>
)
}
}
現在我們重新整理頁面,得到的結果會是:
大致了解 props 的運作之後,我現在要透過呼叫 json 裡的資料,render 到 DOM 上。
我有一個 person
變數,裡面存放著 json 格式的資料
var person = {
name: 'Ya-Yun Tsai',
location: 'Taichung, Taiwan',
occupation: {
title: 'Marketing Intern',
employer: '@voicetube'
},
photo: './images/me.png',
updates: [
{
platform: 'twitter',
status: 'I\'m happy, hope you\'re happy too!'
},
{
platform: 'twitter',
status: 'All about learning is just trying to become a better version of ourselves.'
},
{
platform: 'twitter',
status: 'Feel excited to join this competition!!!!!'
},
{
platform: 'facebook',
status: 'If you\’re working on something that you think is going to get accomplished in this lifetime then you’re not thinking big enough'
}
]
}
我們可以透過 {variable name + name}
來取得這個物件裡的某個值,例如我要取得我的名字,可以透過
{person.name}
要取得 location 則是
{person.location}
現在,我們就把剛剛在 Card component
裡面寫死的值,轉換成呼叫 person 裡面儲存的值。
在更新頁面之後,我遇到一個 bug
這是一個 decoder 的 bug
我點擊它顯示錯誤的網址,它告訴我 bug 詳細資訊
Objects are not valid as a React child (found: object with keys {title, employer}).
這是因為在 occupation
物件裡面有兩個 keys,分別為 title
和 employer
,我在取出 occupation
的值的時候,我寫了 {person.occupation}
,它並不知道我到底是要 title
還是 employer
裡面的值,因此出現了 not valid 的錯誤訊息。
只要把 {person.occupation}
清楚定義,bug 就解決囉~
{person.occupation.title}
現在可以看到 名字、地點、職業
都是從變數 person
裡面取出來的值
可是老師我有問題!!!!(舉手)
如果想要呼叫 occupation
裡面的title
跟 employer
該怎麼做?
在 Card component
裡面,維持 occupation={person.occupation}
<Bio name={person.name} location={person.location} occupation={person.occupation} />
接著到 Bio component
這個 child component 裡面,設定取出更細瑣的值:
<div className="occupation">
<p>{this.props.occupation.title} {this.props.occupation.employer}</p>
</div>
椰~~~現在同時看到 occupation
裡面的title
跟 employer
的資料了!!
updates
裡的資料最後一個階段,我們要來 render updates 裡的資料了!!那兩行 Updates 一直在那邊真的很礙眼。
同樣的,我們可以先在 Card component
裡面,通知它我要取出變數裡 updates 的值了
<Updates updates={person.updates} />
因為在 updates 裡面有 4 個 instance,所以我們要用 map 方法,寫一個迴圈去幫我們取值。
到 Updates component
裡面,新增一個 updates()
,我們要把 mapping 的邏輯寫在這個方法裡面,再把結果 return 回去。把每一個 this.props.updates.map
的結果,塞進 <li></li>
,還記得剛剛提到有 4 個 instance,所以我們應該會回收 4 次的 <li></li>
,裡面放的是 status 的內容。
class Updates extends React.Component{
updates(){
return this.props.updates.map(function(updateInfo){
return(
<li className="update">
{updateInfo.status}
</li>
)
});
}
render (){
return(
<div className="updates">
<ul>
{this.updates()}
</ul>
</div>
)
}
}
我們順利取得 status 的內容~
在 React 官方文件裡提及,當我們使用 map()
這個方法時,它希望我們可以在迴圈的產出項目裡面,設定專屬於每個項目的 key,以便在之後資料集有所更動時,它可以追蹤資料的狀態,是被修改過或者是被刪除等等。
可以想成是寶寶剛出生我們都要幫他們取名字,想像醫院裡的媽媽們是資料集,他們生出來的都是寶寶(就像是我們這邊 loop 出來的 <li></li>
),而護士就像是 React,他們必須知道寶寶的名字,才能更方便照顧他們。因此我們要分別給每個寶寶專屬的 key。
在 <li>
裡面加上 key index
<li className="update" key={index}>
{updateInfo.status}
</li>
記得 function 裡面要多傳一個 index 參數進來
function(updateInfo, index)
最最後一個步驟,我們的 platform
還沒引入進來,會加這個 platform
只是模仿它是從不同來源的資訊,我們可以把它加到 className
裡
<li className={"update " + updateInfo.platform} key={index}>
{updateInfo.status}
</li>
我們可以看到 platform 的資料被我加到 className 裡了~
來張完整的電子名片 <3
椰~花了三篇文章介紹拆解 components 還有 props 的運作原理,很有成就感!
完整專案:Github