本篇筆記摘錄自官方文件 Passing Props to a Component
React 元件 ( component ) 使用 props 來與其他元件溝通,每一個父元件都可以透過 props 傳遞資訊給子元件。
我們可以透過 props 傳遞任何 JavaScript 的值 ( value ) ,包含:物件、陣列、函式。
Familiar props
props 是指傳遞到 JSX 標籤內的資訊,props 本身是一個物件。
舉例來說:className
、src
、alt
、width
、height
… 等,是我們可以傳遞給<img>
的一些 props。
( 將左側拉桿向右移動,可以看見程式碼的部分,拉開後點擊左上角三條線漢堡圖示可以看見完整檔案,右邊則為輸出結果。 )
我們可以傳遞給<img>
的 props 是預先被定義好的 ( ReactDOM 符合 HTML standard ),但是我們可以傳遞任何 props 到自己的元件,像是<Avatar>
。
Passing props to a component
上面的範例中,Profile
元件沒有傳遞任何 props 給它的子元件<Avatar>
:
export default function Profile() {
return (
<Avatar />
);
}
我們可以透過以下兩個步驟來傳遞 props 給Avatar
。
Step 1:Pass props to the child component
首先,傳遞一些 props 到Avatar
。
例如:傳遞兩個 props,一個為person
( object ),一個為size
( number )。
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
現在,我們可以在Avatar
元件中讀取這些 props 了。
如果你對於
person
物件有兩個大括號感到困惑,可以參考 JSX 與 JavaScript 的大括號好朋友 這一篇。
Step 2 : Read props inside the child component
如果想在Avatar
中讀取這些 props,必須要在function Avatar
後面的括號中放入一個大括號,像是這樣:function Avatar({})
。
不同的名稱需用逗號隔開。
function Avatar({ person, size }) {
// person and size are available here
}
接著,在Avatar
中加入一些邏輯,而這邏輯使用person
與size
來渲染。
你可以試著在下方的 sandbox 中改變傳遞給Avatar
的 props,並觀察右邊輸出結果的改變。
props 讓我們將父元件與子元件分開獨立思考。
舉例來說:我們可以在Profile
裡改變person
與size
,但不需要去思考Avatar
會怎麼使用它們。
同樣地,我們也可以改變Avatar
使用這些 props 的方式,不需要去思考Profile
內部的事。
你可以將 props 視為一個可以調整的旋鈕,它們對於函式的角色等同於引數 ( arguments ) 的角色,props 是元件裡的唯一引數!
React 元件函式接受單一引數,也就是一個props
物件:
function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
通常我們不一定會用到整個props
物件,可以使用解構 ( destructure ) 的方式取出需要的部分。
【 Pitfall 】
不要忘記在函式的小括號裡放入大括號:
function Avatar({ person, size }) {
// ...
}
這樣的語法稱為解構 ( destructuring ),等同於從函式參數 ( parameter ) 中讀取屬性 ( properties ) 的方式:
function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
Specifying a default value for a prop
可以為 props 設定預設值,在沒有指定值的時候套用。
在解構的名稱後面加上等號,再將預設值放置於等號右邊:
function Avatar({ person, size = 100 }) {
// ...
}
經過上面設定後,如果在渲染時沒有指定的size
prop,size
將會被設置為100
。
預設值只會使用在以下兩種情形:
1. 找不到size
prop。
2. 傳遞的值為undefined
,像是size={undefined}
。
注意:如果傳遞的值為
size={null}
或是size={0}
,預設值是不會發揮作用的。
Forwarding props with the JSX spread syntax
有些時候,傳遞 props 的行為具有高重複性:
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
上面高重複性的程式碼並沒有錯誤之處,用於理解也非常清楚易懂,但是我們可以讓它變得更簡潔。
有些元件會將所有的 props 都傳遞給它的子元件,就像上方程式碼中Profile
對Avatar
的傳遞。
由於並沒有對這些 props 進行任何直接地操作,只是一個傳遞的行為,因此可以使用展開語法 ( spread syntax ) 讓程式碼變得更簡潔:
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
上述的方法,使我們可以不用列舉出每一個名稱,就能將Profile
的 props 傳遞給Avatar
。
注意:有限度的使用展開語法,如果你在其他每個元件內都使用這個方法,將會發生錯誤。
應該要先弄清楚元件裡的 props 有哪些,而不是每次都直接把整包 props 丟給子元件。
Passing JSX as children
巢狀結構的標籤是常見的:
<div>
<img />
</div>
在元件內也會有相同的情況:
<Card>
<Avatar />
</Card>
在 JSX 標籤中使用巢狀結構撰寫時,父元件將會透過一個名為children
的 prop 接收到內容。
舉例來說:下方的Card
元件,將會收到一個children
prop,也就是Avatar
的設置,並在<div>
內渲染:
試著替換掉<Card>
裡的<Avatar>
,像是隨便打一些文字,再看看右邊的輸出結果,會發現Card
元件可以包覆任何巢狀結構中的內容。
在此方法中,Card
並不需要 “知道” 內層渲染了什麼,這樣彈性的做法是非常常見的。
你可以想像成元件內有一個洞,children
prop 就是那個洞,這個洞可以填入父元件給的 JSX。
How props change over time
下方的Clock
元件從它的父元件接收到兩個 props:color
與time
。
請試著改變右邊輸出結果的選擇顏色欄位:
這個範例說明了,隨著時間的推移,元件可能會接收到不同的 props,這代表著 props 並非靜態!
在範例裡,time
prop 於每一秒改變,color
prop 在我們改變選取時變化,props 反映出即時的元件資料,並非初始資料。
然而,props 是不變的 ( immutable ),這是一個電腦科學裡的術語,意思為 “不可改變的”。
當元件需要改變它的 props,像是回應使用者互動或新資料,它會需要 “請求” 它的父元件傳遞一個不同的 props,一個全新的物件,舊的 props 則會被丟到一旁,最終,JavaScript 引擎將會回收它們。
不要試著去改變 props,如果你需要回應使用者輸入 ( 像是上方範例中的選擇顏色 ),你會需要使用狀態 ( state ) 來設置。
之後會寫官方文件介紹狀態的相關筆記,在還沒生出來之前,可以先看看英文版的官方文件 State: A Component’s Memory. 😜。