傳遞 Props 到 Component 中

React components 使用 props 相互通信。每個 parent component 可以通過向 children component 傳遞 props 來傳遞一些信息。 Props 可能讓你想起 HTML attributes,但你可以通過它們傳遞任何 JavaScript 值,包括 object、array 和函式。

You will learn

  • 如何向 component 傳遞 props
  • 如何從 component 中讀取 props
  • 如何為 props 指定默認值
  • 如何向 component 傳遞一些 JSX
  • props 如何隨著時間變化

認識 props

Props 是你傳遞給 JSX 標籤的信息。例如,classNamesrcaltwidthheight 是你可以傳遞給 <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 符合 the HTML standard 的標準)。但是,你可以向自己的 component(例如 <Avatar>)傳遞任何 props 以自定義它們。以下是如何操作:

向 Component 傳遞 props

在這段代碼中,Profile component 沒有向它的 child component, Avatar 傳遞任何 props:

export default function Profile() {
return (
<Avatar />
);
}

你可以通過兩個步驟為 Avatar 提供一些 props。

第一步:向 child component 傳遞 props

首先,向 Avatar 傳遞一些 props。例如,讓我們傳遞兩個 props:person(一個 object)和 size(一個 number):

export default function Profile() {
return (
<Avatar
person={{ name: 'Lin Lanying', imageId: '1bX5QH6' }}
size={100}
/>
);
}

如果 person= 後面的雙大括號讓你感到困惑,請記住,它們只是 JSX 大括號中的一個 object

現在,你可以在 Avatar component 內部讀取這些 props。

第二步:在 child component 中讀取 props

你可以通過在 function Avatar 之後的 ({}) 內部用逗號分隔它們的名稱 person,size 來讀取這些 props。這使你可以在 Avatar 代碼中使用它們,就像使用變數一樣。

function Avatar({ person, size }) {
// person and size are available here
}

Avatar 添加一些使用 personsize props 進行渲染的邏輯,然後就完成了。

現在,你可以使用不同的 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>
  );
}

Props 讓你獨立思考 parent 和 child components。例如,你可以在 Profile 內部更改 personsize,而無需考慮 Avatar 如何使用它們。同樣的,你可以更改 Avatar 如何使用這些 props,不必考慮到 Profile

你可以將 props 視為可以調節的“旋鈕”。它們扮演著函式所需的參數的相同角色——實際上,props 就是 component 唯一的參數! React component 函式接受一個單獨的參數,一個名為 props 的 object:

function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}

通常你不需要整個 props object 本身,所以你可以使用解構將其拆分成單個 props 變數。

Pitfall

在聲明 props 時,不要忘記() 內部使用一對 {} 大括號

function Avatar({ person, size }) {
// ...
}

此語法稱為 “destructuring” 等同於從函式參數讀取 properties:

function Avatar(props) {
let person = props.person;
let size = props.size;
// ...
}

為 prop 指定默認值

如果你想給一個 prop 一個默認值以在沒有指定值時使用,你可以通過在參數後面放置 = 和默認值來實現解構:

function Avatar({ person, size = 100 }) {
// ...
}

現在,如果在沒有 size prop 的情況下渲染 <Avatar person={...} />,則 size 將被設置為 100

默認值僅在缺少 size prop 或傳遞 size={undefined} 時使用。 但是,如果你傳遞 size={null}size={0},默認值將被使用。

使用 JSX 展開语法轉發 props

有時,傳遞 props 會變得非常重複:

function Profile({ person, size, isSepia, thickBorder }) {
return (
<div className="card">
<Avatar
person={person}
size={size}
isSepia={isSepia}
thickBorder={thickBorder}
/>
</div>
);
}

重複代碼沒有錯——它可以更易讀。 但有時你可能會看重簡潔。 一些 components 將它們的所有 props 轉發給它們的 children component,就像這個 Profile 如何處理 Avatar 一樣。 因為他們不直接使用他們的任何 props,所以使用更簡潔的”展開“語法是有意義的:

function Profile(props) {
return (
<div className="card">
<Avatar {...props} />
</div>
);
}

這將會轉發所有 Profile 的 props 給 Avatar 而無需列出每個 props 的名字。

有節制地使用擴展語法。 如果你在所有其他 component 中都使用它,那就有問題了。 通常,它表示你應該拆分 components 並將 children component 作為 JSX 傳遞。 接下來會詳細介紹!

傳遞 JSX 为 children

嵌套瀏覽器標籤是很常見的:

<div>
<img />
</div>

有時你會希望以相同的方式嵌套自己的 component:

<Card>
<Avatar />
</Card>

當你將內容嵌套在 JSX 標籤中時,parent component 將在名為 children 的 prop 中接收該內容。 例如,下面的 Card component 將接收一個為 <Avatar/>children prop 並將其渲染在被嵌套的 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 component 如何包裝任何嵌套內容。 它不需要“知道”其內部渲染的是什麼。 你會在很多地方看到這種靈活的模式。

你可以將擁有 children prop 的 component 看作是具有可以由其 parent component 用任意 JSX “填充”的“孔”。你通常會使用 children prop 來創建可視化包裝器,例如面板,網格等。

A puzzle-like Card tile with a slot for "children" pieces like text and Avatar

Illustrated by Rachel Lee Nabors

props 如何隨時間變化

下面的 Clock component 從其 parent component 接收兩個 prop:顏色時間。 (省略了 parent component 的代碼,因為它使用了我們暫時不會深入探討的 state。)

嘗試在下面的選擇框中更改顏色:

export default function Clock({ color, time }) {
  return (
    <h1 style={{ color: color }}>
      {time}
    </h1>
  );
}

這個例子說明了一個 component 可能會隨著時間的推移接收到不同的 props。 Props 並不總是靜態的! 在這裡,time prop 每秒都在變化,而 color prop 會在你選擇另一種顏色時發生變化。 Props 反映 component 在任何時間點的數據,而不僅僅是在開始時。

然而,props 是 immutable——計算機科學中的一個術語,意思是“不可改變的”。 當 component 需要更改其 props 時(例如,響應用戶交互或新數據),它將不得不“請求”其 parent component 向它傳遞 不同的 props —— 一個新的 object! 舊的 props 將被丟棄,最終 JavaScript 引擎將回收它們佔用的內存。

不要試圖“改變 props”。 當你需要響應用戶輸入(比如改變選擇的顏色)時,你將需要“設置狀態”,你可以在 State: Component 的記憶。 學習。

Recap

  • 要傳遞 props,將它們添加到 JSX,就像使用 HTML attributes 一樣。
  • 要讀取 props,請使用 function Avatar({ person, size }) 解構語法。
  • 你可以指定一個默認值,例如 size = 100,用於缺失和 undefined 的props。
  • 你可以使用 <Avatar {...props} /> JSX 展開語法轉發所有 props,但不要過度使用它!
  • <Card><Avatar /></Card> 這樣的嵌套 JSX 將顯示為 Card component 的 children props。
  • Props 是時間上的只讀快照:每次渲染都會收到一個新版本的 props。
  • 你不能改變 props。 當你需要交互時,你需要設置 state。

Challenge 1 of 3:
提取 component

這個 Gallery component 包含兩個非常相似的 profile 標籤。 從中提取一個 Profile component 以減少重複。 你需要選擇什麼 props 應該被傳遞。

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 (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>
  );
}