【Vibe Coder 的 React 教學】#03 Props 與 State:資料流的核心概念

測驗:Props 與 State:資料流的核心概念

共 5 題,點選答案後會立即顯示結果

1. 關於 Props 的特性,下列哪個敘述是正確的?

  • A. Props 可以在子元件中被修改
  • B. Props 是元件內部管理的資料
  • C. Props 是從父元件傳遞給子元件的唯讀資料
  • D. Props 可以讓資料從子元件流向父元件

2. 以下程式碼中,useState(0) 的 0 代表什麼?

const [count, setCount] = useState(0);
  • A. 狀態的最大值
  • B. 狀態的初始值
  • C. 狀態的索引位置
  • D. 更新狀態的步進值

3. 為什麼更新 State 時需要建立新的值,而不是直接修改原本的值?

  • A. 因為 JavaScript 不允許修改變數
  • B. 因為這樣可以減少記憶體使用
  • C. 因為 React 會自動防止任何修改操作
  • D. 因為 React 透過比較前後值是否相同來決定是否重新渲染

4. 在「受控元件」模式中,表單輸入框的值是由什麼控制的?

  • A. 由瀏覽器的 DOM 直接控制
  • B. 由 Props 傳入的值控制
  • C. 由 State 控制
  • D. 由 HTML 的 value 屬性固定值控制

5. 下列哪種情況應該使用 State 而非 Props?

  • A. 資料從父元件傳來且不會改變
  • B. 資料會因使用者操作而改變,且在元件內部管理
  • C. 需要讓子元件顯示父元件的資料
  • D. 資料是固定的配置參數

前言

在上一篇文章中,我們學會了用 JSX 建立元件。但你可能會發現,那些元件都是「靜態」的——每次渲染都顯示一樣的內容。

當你用 AI 生成的 React 程式碼越來越複雜,你會開始看到兩個關鍵字不斷出現:PropsState。這兩個概念是 React 資料流的核心,理解它們,你就能看懂大部分 React 程式碼的運作邏輯。

Props:從父元件傳來的資料

Props 是什麼?

Props(Properties 的縮寫)是父元件傳遞給子元件的資料。你可以把它想像成函式的參數。

// 父元件傳遞資料
<Greeting name="小明" />

// 子元件接收資料
function Greeting(props) {
  return <h1>你好,{props.name}!</h1>;
}
Code language: JavaScript (javascript)

當你在 AI 生成的程式碼中看到元件標籤上有 key="value" 的寫法,那就是在傳遞 Props。

解構賦值:更常見的寫法

實務上,你更常看到這種寫法:

function Greeting({ name }) {
  return <h1>你好,{name}!</h1>;
}
Code language: JavaScript (javascript)

這是 JavaScript 的「解構賦值」語法,直接從 props 物件中取出需要的屬性。當 Props 很多時,這種寫法特別清楚:

function UserCard({ name, email, avatar, isOnline }) {
  return (
    <div className="user-card">
      <img src={avatar} alt={name} />
      <h2>{name}</h2>
      <p>{email}</p>
      {isOnline && <span className="online-badge">線上</span>}
    </div>
  );
}
Code language: JavaScript (javascript)

Props 的重要特性:唯讀

Props 是唯讀的,子元件不能修改收到的 Props。這是 React 的設計原則——資料只能從父元件流向子元件,這種「單向資料流」讓程式更容易理解和除錯。

// 錯誤示範 - 不要這樣做!
function Greeting({ name }) {
  name = "小華";  // React 會報錯或行為異常
  return <h1>你好,{name}!</h1>;
}
Code language: JavaScript (javascript)

State:元件內部的可變資料

為什麼需要 State?

Props 是從外部傳入的,但有時候元件需要自己管理一些會變動的資料,例如:

  • 使用者輸入的內容
  • 按鈕的點擊次數
  • 選單是否展開

這就是 State 的用途。

useState Hook 基本用法

React 提供 useState 這個 Hook 來管理狀態:

import { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>目前計數:{count}</p>
      <button onClick={() => setCount(count + 1)}>+1</button>
    </div>
  );
}
Code language: JavaScript (javascript)

讓我們拆解這段程式碼:

const [count, setCount] = useState(0);
//     ↑      ↑              ↑
//   狀態值  更新函式      初始值
Code language: JavaScript (javascript)
  • count:目前的狀態值
  • setCount:用來更新狀態的函式
  • useState(0):設定初始值為 0

狀態更新的不可變性原則

這是新手最容易踩的坑。更新狀態時,你必須建立新的值,而不是直接修改原本的值。

// 基本型別:直接設定新值即可
const [count, setCount] = useState(0);
setCount(count + 1);  // 正確

// 陣列:使用展開運算子建立新陣列
const [items, setItems] = useState(['蘋果', '香蕉']);
setItems([...items, '橘子']);  // 正確:建立新陣列
items.push('橘子');            // 錯誤:直接修改原陣列

// 物件:同樣使用展開運算子
const [user, setUser] = useState({ name: '小明', age: 20 });
setUser({ ...user, age: 21 });  // 正確:建立新物件
user.age = 21;                  // 錯誤:直接修改原物件
Code language: JavaScript (javascript)

為什麼要這樣?因為 React 是透過比較「前後的值是否相同」來決定要不要重新渲染。如果你直接修改原本的物件,React 會認為「值沒變」而不更新畫面。

Props vs State:何時用哪個?

這是初學者最常問的問題。簡單的判斷原則:

情境 使用
資料從父元件傳來 Props
資料在元件內部產生並管理 State
資料不會改變 Props
資料會因使用者操作而改變 State

實際範例:待辦事項

function TodoApp() {
  // State:在這個元件內管理的資料
  const [todos, setTodos] = useState([
    { id: 1, text: '學習 React' },
    { id: 2, text: '寫程式' }
  ]);
  const [inputValue, setInputValue] = useState('');

  const addTodo = () => {
    if (inputValue.trim()) {
      setTodos([...todos, {
        id: Date.now(),
        text: inputValue
      }]);
      setInputValue('');
    }
  };

  return (
    <div>
      <input
        value={inputValue}
        onChange={(e) => setInputValue(e.target.value)}
        placeholder="輸入待辦事項"
      />
      <button onClick={addTodo}>新增</button>

      {todos.map(todo => (
        // Props:傳遞給子元件的資料
        <TodoItem key={todo.id} text={todo.text} />
      ))}
    </div>
  );
}

function TodoItem({ text }) {
  // 這個元件只負責顯示,text 是從 Props 來的
  return <div className="todo-item">{text}</div>;
}
Code language: JavaScript (javascript)

在這個例子中:

  • todosinputValue 是 State,因為它們會隨使用者操作改變
  • text 是 Props,因為它是從 TodoApp 傳給 TodoItem

表單輸入的處理模式

你會在 AI 生成的程式碼中經常看到這種模式:

function LoginForm() {
  const [email, setEmail] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('登入資訊:', { email, password });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={(e) => setEmail(e.target.value)}
        placeholder="電子郵件"
      />
      <input
        type="password"
        value={password}
        onChange={(e) => setPassword(e.target.value)}
        placeholder="密碼"
      />
      <button type="submit">登入</button>
    </form>
  );
}
Code language: JavaScript (javascript)

這種模式叫做「受控元件」(Controlled Component):

  • value={email}:輸入框的值由 State 控制
  • onChange={(e) => setEmail(e.target.value)}:每次輸入都更新 State

這樣 React 就能完全掌控表單的狀態。

Vibe Coding 技巧:請 AI 解釋資料流

當你看到複雜的 React 程式碼,不確定資料怎麼流動時,可以這樣問 AI:

「請解釋這段程式碼的資料流向。哪些是 Props?哪些是 State?資料是怎麼從父元件傳到子元件的?」

或者更具體地:

「這個 user 變數是從哪裡來的?它是 Props 還是 State?在什麼時候會被更新?」

AI 會幫你追蹤資料的來源和流向,這比自己慢慢看要快得多。

常見錯誤與除錯

1. 直接修改 State

// 錯誤
const [items, setItems] = useState([1, 2, 3]);
items.push(4);  // 畫面不會更新!

// 正確
setItems([...items, 4]);
Code language: JavaScript (javascript)

2. 在條件式中使用 Hook

// 錯誤
function MyComponent({ showCount }) {
  if (showCount) {
    const [count, setCount] = useState(0);  // Hook 不能放在條件式裡!
  }
}

// 正確
function MyComponent({ showCount }) {
  const [count, setCount] = useState(0);

  if (!showCount) return null;
  return <p>{count}</p>;
}
Code language: JavaScript (javascript)

3. 忘記設定 key

// 會有警告
{todos.map(todo => (
  <TodoItem text={todo.text} />
))}

// 正確
{todos.map(todo => (
  <TodoItem key={todo.id} text={todo.text} />
))}
Code language: JavaScript (javascript)

本篇重點回顧

  • Props 是父元件傳給子元件的資料,唯讀不可修改
  • State 是元件內部管理的可變資料,用 useState 宣告
  • 更新 State 時要建立新的值,不能直接修改原本的值
  • 從父元件來的資料用 Props,元件自己管理的資料用 State
  • 受控元件模式:用 State 控制表單輸入值

下一步

現在你理解了 React 的資料流核心概念。下一篇我們會學習如何用條件渲染和列表渲染,讓元件根據資料動態顯示不同的內容。

進階測驗:Props 與 State:資料流的核心概念

測驗目標:驗證你是否能在實際情境中應用所學。
共 5 題,包含情境題與錯誤診斷題。

1. 你正在開發一個購物車功能,需要追蹤使用者加入購物車的商品清單。這個清單會隨著使用者的操作而增減。你應該如何管理這個資料? 情境題

  • A. 使用 Props 從父元件傳入商品清單
  • B. 使用 useState 在購物車元件中管理商品陣列
  • C. 直接操作 DOM 來新增或移除商品元素
  • D. 使用全域變數儲存商品清單

2. 小明寫了以下程式碼來新增待辦事項,但點擊按鈕後畫面沒有更新。問題出在哪裡? 錯誤診斷

const [todos, setTodos] = useState([‘學習 React’]); const addTodo = () => { todos.push(‘寫程式’); }; return ( <button onClick={addTodo}>新增</button> );
  • A. useState 的初始值格式錯誤
  • B. onClick 事件綁定方式不正確
  • C. 直接用 push 修改陣列,沒有使用 setTodos 建立新陣列
  • D. 缺少 return 語句導致函式沒有執行

3. 你有一個 UserProfile 元件需要顯示使用者的姓名和頭像。這些資料是從後端 API 取得後,由父元件傳給 UserProfile。你應該在 UserProfile 中如何接收這些資料? 情境題

  • A. 在 UserProfile 中使用 useState 自行管理
  • B. 在 UserProfile 中再次呼叫 API 取得資料
  • C. 使用全域變數存取資料
  • D. 透過 Props 接收父元件傳來的資料

4. 以下程式碼執行時,React 發出警告:「Hooks 的呼叫順序不一致」。問題在哪裡? 錯誤診斷

function ProductCard({ showPrice }) { if (showPrice) { const [price, setPrice] = useState(100); } return <div>商品資訊</div>; }
  • A. useState 的初始值應該是字串而非數字
  • B. Hook 不能放在條件式中,必須在函式最頂層呼叫
  • C. showPrice 參數沒有設定預設值
  • D. 元件名稱首字母應該小寫

5. 你需要開發一個登入表單,包含電子郵件和密碼兩個輸入欄位。你希望在使用者填寫時能即時驗證格式,並在送出時取得所有欄位的值。最佳的實作方式是什麼? 情境題

  • A. 在送出時使用 document.querySelector 取得輸入值
  • B. 只用一個 State 物件管理所有欄位,在送出時才更新
  • C. 使用受控元件模式,為每個欄位建立獨立的 State,並在 onChange 時更新
  • D. 使用 Props 從父元件傳入初始值,讓使用者修改 Props

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *