React 組件使用 *props* 彼此通訊。每個父組件都可以透過給予子組件 props 來傳遞一些資訊。 Props 可能會讓你聯想到 HTML 屬性,但你可以透過它們傳遞任何 JavaScript 值,包括物件、陣列和函式。
你將學到
- 如何將 props 傳遞給組件
- 如何從組件讀取 props
- 如何指定 props 的預設值
- 如何將一些 JSX 傳遞給組件
- props 如何隨著時間改變
熟悉的 props
Props 是您傳遞給 JSX 標籤的資訊。例如,className
、src
、alt
、width
和 height
是您可以傳遞給 <img>
的一些 props
function Avatar() { return ( <img className="avatar" src="https://i.imgur.com/1bX5QH6.jpg" alt="Lin Lanying" width={100} height={100} /> ); } export default function Profile() { return ( <Avatar /> ); }
您可以傳遞給 <img>
標籤的 props 是預先定義的(ReactDOM 符合 HTML 標準)。但是您可以將任何 props 傳遞給*您自己的*組件,例如 <Avatar>
,來自定義它們。方法如下!
將 props 傳遞給組件
在此程式碼中,Profile
組件沒有將任何 props 傳遞給其子組件 Avatar
export default function Profile() {
return (
<Avatar />
);
}
您可以透過兩個步驟將 props 提供給 Avatar
。
步驟 1:將 props 傳遞給子組件
首先,將一些 props 傳遞給 Avatar
。例如,讓我們傳遞兩個 props:person
(一個物件)和 size
(一個數字)
export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}
現在您可以在 Avatar
組件內讀取這些 props。
步驟 2:在子組件內讀取 props
您可以透過在 function Avatar
後面直接的 ({
和 })
內列出以逗號分隔的名稱 person, size
來讀取這些 props。這讓您可以在 Avatar
程式碼內使用它們,就像使用變數一樣。
function Avatar({ person, size }) {
// person and size are available here
}
在 `Avatar
` 元件中加入一些邏輯,使用 `person` 和 `size` 屬性 (props) 來進行渲染,這樣就完成了。
現在你可以使用不同的屬性,以多種不同的方式設定 `Avatar
` 的渲染方式。試試調整看看這些屬性的值吧!
import { getImageUrl } from './utils.js'; function Avatar({ person, size }) { return ( <img className="avatar" src={getImageUrl(person)} alt={person.name} width={size} height={size} /> ); } export default function Profile() { return ( <div> <Avatar size={100} person={{ name: 'Katsuko Saruhashi', imageId: 'YfeOqp2' }} /> <Avatar size={80} person={{ name: 'Aklilu Lemma', imageId: 'OKS67lh' }} /> <Avatar size={50} person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }} /> </div> ); }
屬性讓你能夠獨立思考父元件和子元件。例如,你可以在 `Profile
` 中更改 `person` 或 `size` 屬性,而無需考慮 `Avatar
` 如何使用它們。同樣地,你可以更改 `Avatar
` 使用這些屬性的方式,而無需查看 `Profile
`。
你可以將屬性想像成可以調整的「旋鈕」。它們的作用與函式的參數相同——事實上,屬性 *就是* 你元件的唯一參數!React 元件函式接受單個參數,一個 `props
` 物件。
function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}
通常你不需要整個 `props
` 物件本身,因此你可以將其解構為個別的屬性。
指定屬性的預設值
如果你想給一個屬性一個預設值,以便在未指定值時使用,你可以在解構時,在參數後面加上 `=` 和預設值。
function Avatar({ person, size = 100 }) {
// ...
}
現在,如果 `<Avatar person={...} />
` 在渲染時沒有 `size` 屬性,`size` 將會被設定為 `100
`。
只有在缺少 `size` 屬性或傳遞 `size={undefined}` 時,才會使用預設值。但如果你傳遞 `size={null}` 或 `size={0}`,則**不會**使用預設值。
使用 JSX 展開語法傳遞屬性
有時,傳遞屬性會變得非常重複。
function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}
重複的程式碼本身沒有錯——它可以更易讀。但在某些時候,你可能會更重視簡潔性。有些元件會將其所有屬性都轉發給其子元件,就像這個 `Profile
` 對 `Avatar
` 所做的一樣。因為它們沒有直接使用任何自己的屬性,所以使用更簡潔的「展開」語法是有意義的。
function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}
這會將 `Profile
` 的所有屬性都轉發給 `Avatar
`,而無需列出每個屬性的名稱。
**謹慎使用展開語法。** 如果你在每個元件中都使用它,那就表示哪裡出錯了。通常,這表示你應該拆分你的元件,並將子元件作為 JSX 傳遞。接下來會詳細說明!
將 JSX 作為子元件傳遞
巢狀內建瀏覽器標籤是很常見的。
<div>
<img />
</div>
有時你會想要以相同的方式巢狀你自己的元件。
<Card>
<Avatar />
</Card>
當你在 JSX 標籤內巢狀內容時,父元件將會在名為 `children` 的屬性中收到該內容。例如,下面的 `Card
` 元件將會收到一個設定為 `<Avatar />
` 的 `children` 屬性,並將其渲染在一個包裝器 `div` 中。
import Avatar from './Avatar.js'; function Card({ children }) { return ( <div className="card"> {children} </div> ); } export default function Profile() { return ( <Card> <Avatar size={100} person={{ name: 'Katsuko Saruhashi', imageId: 'YfeOqp2' }} /> </Card> ); }
試著將 `<Card>
` 內的 `<Avatar>
` 替換成一些文字,看看 `Card
` 元件如何包裝任何巢狀的內容。它不需要「知道」它裡面渲染的是什麼。你會在很多地方看到這種彈性的模式。
你可以將具有 `children` 屬性的元件想像成有一個「洞」,可以由其父元件使用任意 JSX 來「填補」。你經常會將 `children` 屬性用於視覺包裝器:面板、網格等等。

插圖作者: Rachel Lee Nabors
屬性如何隨時間變化
下面的 `Clock
` 元件從其父元件接收兩個屬性:`color` 和 `time`。(父元件的程式碼被省略,因為它使用了狀態,我們現在還不會深入探討。)
嘗試更改下方選擇框中的顏色
export default function Clock({ color, time }) { return ( <h1 style={{ color: color }}> {time} </h1> ); }
這個例子說明了一個組件可能會隨著時間收到不同的屬性。屬性並非一成不變!這裡,time
屬性每秒都會變化,而 color
屬性則會在您選擇其他顏色時發生變化。屬性反映了組件在任何時間點的數據,而不僅僅是在開始時。
然而,屬性是不可變的——一個來自電腦科學的術語,意思是「不可更改的」。當一個組件需要更改其屬性時(例如,響應使用者互動或新的數據),它必須「請求」其父組件傳遞給它不同的屬性——一個新的物件!它的舊屬性將被捨棄,最終 JavaScript 引擎將回收它們佔用的記憶體。
不要嘗試「更改屬性」。當您需要響應使用者輸入(例如更改所選顏色)時,您需要「設定狀態」,您可以在狀態:組件的記憶體中學習。
重點回顧
- 要傳遞屬性,請將它們添加到 JSX 中,就像使用 HTML 屬性一樣。
- 要讀取屬性,請使用
function Avatar({ person, size })
解構語法。 - 您可以指定一個預設值,例如
size = 100
,它用於缺少的和undefined
的屬性。 - 您可以使用
<Avatar {...props} />
JSX 展開語法轉發所有屬性,但不要濫用它! - 巢狀的 JSX,例如
<Card><Avatar /></Card>
將會以Card
組件的children
屬性出現。 - 屬性是時間上的唯讀快照:每次渲染都會收到新的屬性版本。
- 您無法更改屬性。當您需要互動性時,您需要設定狀態。
挑戰 1之 3: 提取組件
這個 Gallery
組件包含兩個個人檔案的一些非常相似的標記。從中提取一個 Profile
組件以減少重複。您需要選擇要傳遞給它的屬性。
import { getImageUrl } from './utils.js'; export default function Gallery() { return ( <div> <h1>Notable Scientists</h1> <section className="profile"> <h2>Maria Skłodowska-Curie</h2> <img className="avatar" src={getImageUrl('szV5sdG')} alt="Maria Skłodowska-Curie" width={70} height={70} /> <ul> <li> <b>Profession: </b> physicist and chemist </li> <li> <b>Awards: 4 </b> (Nobel Prize in Physics, Nobel Prize in Chemistry, Davy Medal, Matteucci Medal) </li> <li> <b>Discovered: </b> polonium (chemical element) </li> </ul> </section> <section className="profile"> <h2>Katsuko Saruhashi</h2> <img className="avatar" src={getImageUrl('YfeOqp2')} alt="Katsuko Saruhashi" width={70} height={70} /> <ul> <li> <b>Profession: </b> geochemist </li> <li> <b>Awards: 2 </b> (Miyake Prize for geochemistry, Tanaka Prize) </li> <li> <b>Discovered: </b> a method for measuring carbon dioxide in seawater </li> </ul> </section> </div> ); }