iT邦幫忙

2026 iThome 鐵人賽

DAY 7
0
自我挑戰組

程式碼 AI 寫,我負責看懂:30 天拆解 React × TypeScript系列 第 7

Day 07|Vue 有 Props,React 也有 Props:真的一樣嗎?

  • 分享至 

  • xImage
  •  

React 的 Props 是什麼?

在上一篇的認識 JSX 中有出現這麼段程式碼:

// App.tsx
<TodoList
  todos={todos}
  onToggle={toggleTodo}
  onDelete={deleteTodo}
/>

當時只知道 <TodoList /> 是自己建立的 Component,但標籤裡的 todosonToggleonDelete 又是什麼呢?

它們都是傳給 TodoList 的 Props。

Props 是 Properties 的縮寫,可以讓一個 Component 接收外部提供的資料。React Component 會使用 Props 彼此溝通,外層 Component 可以透過 Props 將資訊傳給內層 Component。
React 官方文件:將 Props 傳遞給 Component

在這段程式碼中,App 使用了 TodoList,並透過 Props 提供三個值:

  • todos:待辦事項陣列
  • onToggle:切換完成狀態的函式
  • onDelete:刪除待辦事項的函式

因此,可以先把 Props 理解成:

使用 Component 時,從外部提供給它的資料。

React 如何傳入 Props?

在 React 中,可以透過 JSX 標籤上的屬性傳入 Props。

例如:

<TodoList todos={todos} />

這表示 TodoList 會收到一個名為 todos 的 Prop,而它的值是大括號裡的 todos 變數。

如果要一次傳入多個 Props,就可以繼續寫在 Component 標籤上:

<TodoList
  todos={todos}
  onToggle={toggleTodo}
  onDelete={deleteTodo}
/>

這種寫法看起來有點像在 HTML 標籤上加入屬性,但 React Props 的值不只能是文字,也可以是各種 JavaScript 值。

例如:

<Component
  title="Todo List"
  count={3}
  completed={false}
  todos={todos}
  onDelete={deleteTodo}
/>

這些 Props 分別傳入了:

  • 字串
  • 數字
  • 布林值
  • 陣列
  • 函式

如果傳入的是固定字串,可以直接寫成:

title="Todo List"

如果傳入的是 JavaScript 運算式,則要使用大括號:

count={3}
completed={false}
todos={todos}
onDelete={deleteTodo}

因此,Props 並不只是一組寫在 Component 標籤上的文字,而是可以將各種 JavaScript 值提供給另一個 Component。

React Component 如何接收 Props?

外層的 App 傳入 Props 後,TodoList 需要透過 function 的參數接收。
目前專案中的 TodoList.tsx 是這樣寫:

function TodoList({ todos, onToggle, onDelete }: TodoListProps) {
  // ...
}

這裡的 : TodoListProps 是 TypeScript 的型別標註,用來告訴 TypeScript「這個 Component 會收到哪些 Props、每個 Props 是什麼型別」。TodoListProps 本身是另外定義好的 interface(這邊目前先不深入解釋,打算留到之後的某一篇幅再說。)
現在只需要知道:拿掉 : TodoListProps 之後,剩下的 { todos, onToggle, onDelete } 才是這篇文章真正要講的物件解構部分。

為什麼 Props 會出現在 function 的參數裡?
因為 React Component 本身就是一個 function。React Component function 會接收一個參數,也就是包含所有傳入資料的 Props 物件。

如果不使用目前專案中的物件解構,也可以直接透過 props 接收:

function TodoList(props) {
  console.log(props.todos)
  console.log(props.onToggle)
  console.log(props.onDelete)
}

這裡的 props 可以想成一個物件:

const props = {
  todos: todos,
  onToggle: toggleTodo,
  onDelete: deleteTodo,
}

因此,要取得其中的 todos,可以使用:

props.todos

要取得另外兩個 Props,則可以使用:

props.onToggle
props.onDelete

這就是 React Component 接收 Props 的基本方式。

為什麼專案裡沒有一直寫 props.

前面說這麼多但卻沒看到有寫props.todos對叭
因為專案中的 TodoList 並不是透過 props.todos 取得資料,而是直接使用function 的參數使用了 JavaScript 的物件解構。

todos

如果不使用物件解構,會寫成:

function TodoList(props) {
  console.log(props.todos)
  console.log(props.onToggle)
  console.log(props.onDelete)
}

使用物件解構後,可以直接從 Props 物件中取出需要的內容:

function TodoList({ todos, onToggle, onDelete }) {
  console.log(todos)
  console.log(onToggle)
  console.log(onDelete)
}

所以,下面兩種寫法取得的是相同的資料:

props.todos
todos

使用物件解構後,就不需要在每次使用時都寫上 props.

回頭看目前專案的寫法:

function TodoList({ todos, onToggle, onDelete }: TodoListProps) {
  if (todos.length === 0) {
    return <p className="empty">目前沒有待辦事項,新增一筆看看吧!</p>
  }

  // ...
}

TodoList 可以直接使用收到的 todos,取得待辦事項的數量。

Props 的名稱是自己決定的嗎?

<TodoList todos={todos} />

第一次看到這段時只覺得有看沒有懂,越看越霧煞煞,為什麼左右兩邊都是 todos??
雖然名稱相同,但它們代表的角度不同:

todos={todos}
  ↑       ↑
Prop 名稱  App 裡的變數

左邊的 todos 是 Prop 的名稱,也就是 TodoList 接收時使用的名稱;右邊大括號裡的 todos,則是 App 裡目前要傳入的 JavaScript 變數。

這段程式碼可以理解成:

TodoListtodos Prop = App 裡的 todos 變數

左右兩邊不一定要使用相同名稱,只是目前專案剛好取了相同的名字,所以才容易讓人誤會兩者是同一件事。

假設 App 裡的變數叫做 todoItems

const todoItems = []

使用 TodoList 時可以寫成:

<TodoList todos={todoItems} />

這代表:

TodoListtodos Prop = App 裡的 todoItems 變數

TodoList 接收時仍然使用 todos

function TodoList({ todos }) {
  console.log(todos)
}

Prop 的名稱是由開發者決定的,但傳入與接收時使用的 Prop 名稱必須互相對應

如果傳入時改成:

<TodoList items={todos} />

接收時就要使用 items

function TodoList({ items }) {
  console.log(items)
}

React 會根據 Prop 名稱把資料放進 Props 物件,所以傳入與接收時使用的 Prop 名稱必須一致,與外層變數的名稱是否相同無關。

這邊專案中使用 todos,是因為這個名稱可以直接表達它是一組待辦事項。
另外兩個 Props 使用 onToggleonDelete,也可以看出它們提供的是與切換、刪除有關的函式。
清楚的 Props 名稱,可以讓人直接從 Component 的使用方式,大致看懂它需要哪些資料。

Props 可以繼續傳給其他 Component

App 將 Props 傳給 TodoList

<TodoList
  todos={todos}
  onToggle={toggleTodo}
  onDelete={deleteTodo}
/>

TodoList 接收後,也可以將收到的內容繼續傳給 TodoItem

<TodoItem
  key={todo.id}
  todo={todo}
  onToggle={onToggle}
  onDelete={onDelete}
/>

在這段程式碼中,TodoList 傳給 TodoItem 三個 Props:

  • todo
  • onToggle
  • onDelete

接著,TodoItem 再透過 function 的參數接收:

function TodoItem({ todo, onToggle, onDelete }: TodoItemProps) {
  // ...
}

專案裡的 Props 傳遞可以看成:

App
  ↓ Props
TodoList
  ↓ Props
TodoItem

每個 Component 只要透過 function 的參數接收,就可以使用外部提供的資料。

程式碼中的 key 是 React 用來辨認清單項目的特殊屬性,不會像一般 Props 一樣傳給 TodoItem,所以 TodoItem 接收到的是 todoonToggleonDelete

Props 不應該被直接修改

Props 是外部提供給 Component 的資料,接收 Props 的 Component 不應該直接修改它。

例如,TodoList 收到 todos

function TodoList({ todos }) {
  // ...
}

不應該直接在 TodoList 裡修改這個陣列:

todos.push(newTodo)

可以將 Props 理解成 Component 在每次 Render 時收到的一份唯讀資料。Component 可以讀取並使用它,但不應該直接修改它。

例如,可以讀取陣列長度:

todos.length

也可以讀取其中一筆資料:

todo.text

Props 本身不會自己改變——它更像是外層在某個時間點交給 Component 的一份快照。當外層傳入新的值時,Component 不會「修改」手上這份快照,而是在下一次 Render 收到一份新的快照。

React 官方文件將 Props 形容為每次 Render 收到的唯讀快照,因此收到 Props 的 Component 不應該直接改變它。React 官方文件:Props 如何隨時間改變 (這樣的限制可以讓資料的來源與管理權責更加清楚。)

Vue 與 React 的 Props

回頭看看 Vue Todo List 專案,App.vue 傳入 todos 的寫法是:

<TodoList :todos="todos" />

React Todo List 專案則是:

<TodoList todos={todos} />

這兩段程式碼做的事情很接近,都是將外層的 todos 變數傳給 TodoList,主要差別是語法。
接收 Props 的方式也不同。

Vue 的 TodoList.vue 使用 defineProps()

<script setup>
defineProps({
  todos: {
    type: Array,
    required: true,
  },
})
</script>

React 的 TodoList 則透過 function 的參數接收:

function TodoList({ todos }) {
  // ...
}

將兩者放在一起比較:

Vue React
使用 :todos="todos" 傳入變數 使用 todos={todos} 傳入變數
使用 defineProps() 宣告 Props 使用 function 參數接收 Props
在 Template 中使用 todos 在 Component function 中使用 todos

兩者的共通點是:Props 都不應該被接收方直接修改,這一點無論在 Vue 或 React 都一樣,是資料單向流動的基本原則。
所以 Vue 與 React 的 Props 在概念上很相似,都是讓 Component 接收外部資料。
這部分對我來說,應該需要適應的不是 Props 的用途,而是 React 不使用 defineProps(),直接將 Props 交給 Component function。

Vue 有 Props,React 也有 Props:真的一樣嗎?

如果只看用途,它們確實很接近:

都是讓一個 Component 接收外部提供的資料。

不過,因為 Vue 和 React 建立 Component 的方式不同,所以 Props 的語法也不一樣。

Vue 會先使用 defineProps() 宣告:

defineProps({
  todos: {
    type: Array,
    required: true,
  },
})

React 則直接從 function 的參數接收:

function TodoList({ todos }) {
  // ...
}

React Props 目前我先簡單理解成:

使用 Component 時,寫在 JSX 標籤上並傳給 Component function 的資料。

今日回顧

今天從 React Todo List 專案認識了 Props:

  • Props 是 Properties 的縮寫
  • Props 可以讓 Component 接收外部提供的資料
  • React 透過 JSX 標籤上的屬性傳入 Props
  • Component 透過 function 的參數接收 Props
  • Component function 會接收一個 Props 物件
  • 可以使用 props.todos 取得其中的資料
  • 也可以透過物件解構直接取得需要的 Props
  • Props 可以是字串、數字、布林值、陣列或函式
  • Prop 名稱必須和接收時使用的名稱互相對應
  • 收到 Props 的 Component 可以繼續將它傳給其他 Component
  • Props 是每次 Render 時收到的唯讀快照
  • Component 不應該直接修改收到的 Props
  • Vue 與 React 的 Props 用途相近,但語法不同

回頭再看原本覺得陌生的程式碼:

<TodoList
  todos={todos}
  onToggle={toggleTodo}
  onDelete={deleteTodo}
/>

現在知道這段程式碼是在使用 TodoList 時,將三個 Props 傳給它。

TodoList 透過 function 的參數接收:

function TodoList({ todos, onToggle, onDelete }: TodoListProps) {
  // ...
}

Vue 有 Props,React 也有 Props。兩者想解決的問題確實很像,只是到了 React,Props 會直接進入 Component function,成為 Component 描述畫面時可以使用的資料。

這篇先這樣,不小心打的落落長
下一篇 Day 08 預計來了解一下 React 的 map() 下篇見!


上一篇
Day 06|HTML 寫進 JavaScript?第一次理解 JSX
系列文
程式碼 AI 寫,我負責看懂:30 天拆解 React × TypeScript7
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言