iT邦幫忙

2026 iThome 鐵人賽

DAY 4
0
自我挑戰組

React 入門到實作與除錯|30 天哩ㄟ刻系列 第 4

Day 4 : 以不變應萬變,讓元件接收不同資料

  • 分享至 

  • xImage
  •  

原則上就是,以我為原則

今天目標

  1. 認識 props,外層元件傳遞資料給內層元件
  2. 用同一個元件顯示三份不同內容
  3. 練習解構與預設值
  4. 使用 children
  5. 測試執行
  6. 錯誤修正練習

今天任務多了一點,要測試執行以及嘗試錯誤修正,當元件需要跟外面的資料互動,問題也會隨不同的資料浮現。/images/emoticon/emoticon16.gif

開啟昨天修改好的 welcome.tsx,開始囉。

認識 props

之前的資料是在 Welcome 裡直接宣告:

export function Welcome() {
  const topic = "JSX";
  const day = 3;
  const totalDays = 30;
  return (
      <main className="min-h-screen bg-gray-100 px-6 py-10 text-gray-900">
        <section className="mx-auto max-w-xl space-y-4 rounded-xl bg-white p-6 shadow">
      //...略

如果想重複使用這個版型與樣式,建立元件就好,但如果同一個畫面要出現多個相同元件,且內容要不一樣,就要改很多數字,這時候可以使用 props

來個小範例:

type LearningCardProps = {
  title: string;
};

function LearningCard(props: LearningCardProps) {
  return <h2>{props.title}</h2>;
}

props 是一個物件,props.title 表示讀取其中的 title。
LearningCardProps 是 TypeScript 的 type 型別。

也可以通過 解構 取出資料欄位,變成:

function LearningCard({ title }: LearningCardProps) {
  return <h2>{title}</h2>;
}

注意 ({ title }) 裡的大括號不要漏掉。元件接收的是一個 props 物件,不是把每個欄位當成不同的函式參數。

回想 HTML Tag,通常可以放 style、class 等參數,props 讓開發者建立需要的參數。
我們預期要把上面的卡片變成一行的 Tag,像是:
<LearningCard title="我的 React 練習" topic="props" day={4} />

title、topic、day 是我們替這個元件取的資料名稱。接收的名稱也要一致。

這裡要注意的是,使用 {} 和雙引號是不同的意思,看表格:

寫法 傳遞的意思
topic="props" 字串 "props"
day={4} 數字 4
day="4" 字串 "4"
topic={topic} 讀取父元件中名為 topic 的變數,左邊是 prop 名稱,右邊是變數

以原本的 Welcome 為例,先提取我們要的內容為元件。

function LearningCard() {
  const topic = "JSX";
  const day = 3;
  const totalDays = 30;
  const remainingDays = totalDays - day;
  
  return (
    <section className="mx-auto max-w-xl space-y-4 rounded-xl bg-white p-6 shadow">
      <h1 className="text-2xl font-bold">我的 React 學習卡片</h1>
      <p className="text-gray-600">
        今天練習 {topic}
        <br />
        先讓畫面長出來。
      </p>
      <hr className="border-gray-200" />
      <h2 className="font-bold text-blue-800">學習進度</h2>
        <p className="mt-2">
          目前進度:第 {day} 天 / 共 {totalDays} 天
        </p>
        <p>接下來還有 {remainingDays} 天</p>
      <MyButton />
    </section>
  );
}

提取參數,用 TypeScript 的 type 來建立。

type LearningCardProps = {
  title: string;
  topic: string;
  day: number;
  totalDays?: number; // 可以省略;預設值在接收參數時設定。
};

別忘了原本的卡片有大客戶想要的按鈕,在 React 可以使用 children
將標籤內的內層元素放進卡片裡面。

children?: ReactNode 描述這個資料欄位,放進上面的 LearningCardProps

ReactNode 是 React 提供的型別,可以描述 JSX、文字等可渲染內容,加上問號,表示卡片也可以不放額外內容。React:Typing children

組合起來

import type { ReactNode } from "react"; // 使用到 ReactNode 型別,要 import react

type LearningCardProps = {
  title: string;
  topic: string;
  day: number;
  totalDays?: number;
  children?: ReactNode;
};

function LearningCard({
  title,
  topic,
  day,
  totalDays = 30,
  children,
}: LearningCardProps) {
  const remainingDays = totalDays - day;

  return (
    <section className="space-y-4 rounded-xl bg-white p-6 shadow">
      <h2 className="text-xl font-bold">{title}</h2>
      <p className="text-gray-600">今天練習 {topic}</p>
      <hr className="border-gray-200" />
      <h3 className="font-bold text-blue-800">學習進度</h3>
      <p>
        目前進度:第 {day} 天 / 共 {totalDays} 天
      </p>
      <p>接下來還有 {remainingDays} 天</p>
      {children}
    </section>
  );
}

<LearningCard /> 元件應用到 Welcome.tsx

import type { ReactNode } from "react";

type LearningCardProps = {
  title: string;
  topic: string;
  day: number;
  totalDays?: number;
  children?: ReactNode;
};

export function Welcome() {
  return (
    <main className="min-h-screen bg-gray-100 px-6 py-10 text-gray-900">
      <div className="mx-auto max-w-xl space-y-6">
        <h1 className="text-2xl font-bold">我的 React 學習卡片</h1>

        <LearningCard title="我的 React 練習" topic="props" day={4}>
          <MyButton />
        </LearningCard>

        <LearningCard title="小明的 React 練習" topic="JSX" day={3}>
          <p className="text-gray-600">先把 JSX 的大括號弄清楚。</p>
        </LearningCard>

        <LearningCard
          title="小美的短期練習"
          topic="元件"
          day={2}
          totalDays={7}
        >
          <p className="text-gray-600">
            今天的筆記:<strong>元件可以重複使用。</strong>
          </p>
        </LearningCard>
      </div>
    </main>
  );
}

function LearningCard({
  title,
  topic,
  day,
  totalDays = 30,
  children,
}: LearningCardProps) {
  const remainingDays = totalDays - day;

  return (
    <section className="space-y-4 rounded-xl bg-white p-6 shadow">
      <h2 className="text-xl font-bold">{title}</h2>
      <p className="text-gray-600">今天練習 {topic}</p>
      <hr className="border-gray-200" />
      <h3 className="font-bold text-blue-800">學習進度</h3>
      <p>
        目前進度:第 {day} 天 / 共 {totalDays} 天
      </p>
      <p>接下來還有 {remainingDays} 天</p>
      {children}
    </section>
  );
}

function MyButton() {
  return (
    <button
      className="cursor-pointer rounded bg-blue-500 px-3 py-2 text-white hover:bg-blue-600"
      type="button"
    >
      按鈕來了
    </button>
  );
}

測試執行

https://ithelp.ithome.com.tw/upload/images/20260918/20184332nKxCgsQFSr.png

現在畫面上有三張卡片,程式裡只定義了一次 LearningCard,以後要改樣式就可以統一變動了。

錯誤修正練習

現在來演練錯誤問題發生

傳遞型別錯誤

<LearningCard title="我的 React 練習" topic="props" day="4">

day 要接收數字,使用大括號傳入 4,不要用引號(會變成字串)。

<LearningCard title="我的 React 練習" topic="props" day={4}>

children傳了卻沒有顯示

function LearningCard({ title, children }: LearningCardProps) {
  return (
    <section className="space-y-4 rounded-xl bg-white p-6 shadow">
      <h2 className="text-xl font-bold">{title}</h2>
    </section>
  );
}

因為裡面少放了,加上去就好。

function LearningCard({ title, children }: LearningCardProps) {
  return (
    <section className="space-y-4 rounded-xl bg-white p-6 shadow">
      <h2 className="text-xl font-bold">{title}</h2>
      {children}
    </section>
  );
}

沒有停止條件的互相渲染 = 無限迴圈

宇宙的外面有一個宇宙的外面有一個...
React:Render and Commit

範例:

function LearningCard({ title }: LearningCardProps) {
  return (
    <section className="space-y-4 rounded-xl bg-white p-6 shadow">
      <h2 className="text-xl font-bold">{title}</h2>
      <Welcome />
    </section>
  );
}

如果用別的元件,在別的元件也使用到了外層元件,那就會形成 間接遞迴
因為這些無限遞迴範例仍可通過型別檢查,必須另外檢查渲染路徑與停止條件,盡量少使用。/images/emoticon/emoticon13.gif


今天把一張卡片改成可重複使用的元件,資料能各自傳入,外觀統一調整。

參考資料


上一篇
Day 3 : 改頭換面,用 JSX 重寫畫面
下一篇
Day 5 : 見機行事,條件判斷決定顯示內容
系列文
React 入門到實作與除錯|30 天哩ㄟ刻9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言