【 React 】元件與 Props

Jamie Lo
8 min readJan 8, 2023

--

Photo by Kevin Schmid on Unsplash

本篇筆記摘錄自官方文件 Passing Props to a Component

React 元件 ( component ) 使用 props 來與其他元件溝通,每一個父元件都可以透過 props 傳遞資訊給子元件。

我們可以透過 props 傳遞任何 JavaScript 的值 ( value ) ,包含:物件、陣列、函式。

Familiar props

props 是指傳遞到 JSX 標籤內的資訊,props 本身是一個物件。

舉例來說:classNamesrcaltwidthheight… 等,是我們可以傳遞給<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中加入一些邏輯,而這邏輯使用personsize來渲染。

你可以試著在下方的 sandbox 中改變傳遞給Avatar的 props,並觀察右邊輸出結果的改變。

props 讓我們將父元件與子元件分開獨立思考。

舉例來說:我們可以在Profile裡改變personsize,但不需要去思考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 }) {
// ...
}

經過上面設定後,如果在渲染時沒有指定的sizeprop,size將會被設置為100

預設值只會使用在以下兩種情形:
1. 找不到sizeprop。
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 都傳遞給它的子元件,就像上方程式碼中ProfileAvatar的傳遞。

由於並沒有對這些 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元件,將會收到一個childrenprop,也就是Avatar的設置,並在<div>內渲染:

試著替換掉<Card>裡的<Avatar>,像是隨便打一些文字,再看看右邊的輸出結果,會發現Card元件可以包覆任何巢狀結構中的內容。

在此方法中,Card並不需要 “知道” 內層渲染了什麼,這樣彈性的做法是非常常見的。

你可以想像成元件內有一個洞,childrenprop 就是那個洞,這個洞可以填入父元件給的 JSX。

How props change over time

下方的Clock元件從它的父元件接收到兩個 props:colortime

請試著改變右邊輸出結果的選擇顏色欄位:

這個範例說明了,隨著時間的推移,元件可能會接收到不同的 props,這代表著 props 並非靜態

在範例裡,timeprop 於每一秒改變,colorprop 在我們改變選取時變化,props 反映出即時的元件資料,並非初始資料。

然而,props 是不變的 ( immutable ),這是一個電腦科學裡的術語,意思為 “不可改變的”。

當元件需要改變它的 props,像是回應使用者互動或新資料,它會需要 “請求” 它的父元件傳遞一個不同的 props,一個全新的物件,舊的 props 則會被丟到一旁,最終,JavaScript 引擎將會回收它們。

不要試著去改變 props,如果你需要回應使用者輸入 ( 像是上方範例中的選擇顏色 ),你會需要使用狀態 ( state ) 來設置。

之後會寫官方文件介紹狀態的相關筆記,在還沒生出來之前,可以先看看英文版的官方文件 State: A Component’s Memory. 😜。

--

--

Jamie Lo

正在往前端這個知識量爆炸的黑洞前行,內容多為平時的筆記整理,希望也能幫助到同樣在這條道路上前進的人💪