iT邦幫忙

2026 iThome 鐵人賽

DAY 22
0
Build on Google AI

今天學什麼?30 天用 Google AI 打造智慧學習卡系列 第 22 篇

Day 22|AI 不一定每次都成功:替 Learning Card 加上 Loading 與 Error 狀態

  • 分享至 

  • xImage
  •  

昨天 Day 21,我終於把 Gemini Structured Output 真正接進 React。

原本停在 Console 裡的 JSON,現在已經可以:

Gemini Response
→ response.cards
→ LearningCard[]
→ React State
→ Learning Card UI

輸入不同的學習對象、主題與卡片數量後,Gemini 產生的內容就會直接變成畫面上的學習卡。

不過真的把 Gemini API 接進 App 後,還有另一個很實際的問題:

Gemini 不一定每次都會立刻成功。

如果 API 還在處理,使用者需要知道目前正在等待;如果 API 呼叫失敗,也不能讓整個 App 跟著壞掉。

所以今天先不增加新的 AI 功能,而是回頭檢查並補強 Loading 與 Error Handling。


Day 21 其實已經有 Loading State

一開始整理今天的程式時,我才發現 Day 21 其實已經有:

const [isLoading, setIsLoading] = useState<boolean>(false);
const [errorMessage, setErrorMessage] = useState<string | null>(null);

而且送出表單時也已經會:

setIsLoading(true);
setErrorMessage(null);

最後透過:

finally {
  setIsLoading(false);
}

把 Loading 關掉。

表單上的年齡、主題、卡片數量與產生按鈕,也已經搭配:

disabled={isLoading}

所以 Day 22 並不是「從零加入 Loading」。

比較準確地說,今天要做的是:

把原本已經存在的 Loading / Error 流程補強,並且真的把 Success、Loading、Error 三種情況跑過一次。


今天想完成什麼?

今天主要確認三件事情:

正常產生
→ Loading → Success → 更新 Cards

產生途中
→ 鎖定生成表單 → 避免重複送出

產生失敗
→ Loading → Error → 保留原本 Cards

也就是開始處理 Gemini API 不只「成功」的情況。


這次我怎麼請 Gemini 修改?

這次仍然使用 Google AI Studio 的 Gemini 3.8 Flash。

我的重點是要求它保留 Day 21 已經完成的資料流程,不要重新修改 Structured Output、Prompt Template 或 LearningCard[]。

主要希望補強:

Loading 狀態
Error 狀態
防止重複 Submit
錯誤訊息處理
API 失敗時保留既有 Cards

Day22 New Prompt

Gemini 執行結果:

Gemini 3.8 Flash
Ran for 91s

Edited 1 file

src/components/CardGeneratorForm.tsx

Built

這次只修改一個 Component。

Day22 Gemini model response


Gemini 實際修改了什麼?

比對 Day 21 與 Day 22 後,isLoading 與 errorMessage 本來就已經存在。

這次比較明顯的改動,是增加流程 Log:

console.log('Submit');
console.log('Loading');
console.log('Success');
console.log('Error');

讓我可以直接從 Console 觀察:

Submit
→ Loading
→ Success

或:

Submit
→ Loading
→ Error

另外也補上 Response 解析失敗的處理:

let data: any;

try {
  data = await res.json();
} catch {
  throw new Error(`伺服器回應格式異常 (${res.status})`);
}

Error Message 也增加了網路錯誤、503、API Key、必要參數等不同情況的處理。

Loading 時按鈕則會顯示:

AI 正在產生學習卡...

接著就實際測看看。

Day22 CardGeneratorForm update


Test A|正常產生:交通工具 × 3 張

第一組測試:

學習對象:5~6 歲幼兒
學習主題:交通工具
卡片數量:3

Day22 產生新學習卡5-6歲交通工具3張

送出後,畫面先進入:

AI 正在產生學習卡...

Day22 new card 5-6 transportation 3 cards loading

Console 則依序出現:

Submit
Loading
Success

接著 Day 21 的資料流程也正常執行:

Gemini Structured Response
→ response.cards
→ LearningCard[]
→ React State

最後成功產生:

🚗 小汽車
🚌 公車
🚂 火車

畫面更新成:

✨ 交通工具 1 / 3

代表補強 Loading / Error Handling 後,原本 Gemini → React 的流程仍然正常。

Day22 new cards 5-6 transportaion 3 cards


Test B|Loading 時還能繼續操作嗎?

接著我在 Gemini 還沒回應時,故意嘗試操作表單。

結果 Loading 期間:

再次產生學習卡     ❌
切換學習對象       ❌
修改 Topic         ❌
快速選擇主題       ❌
切換 3 / 5 / 7 張  ❌

這部分其實 Day 21 就已經透過 disabled={isLoading} 實作,今天則是真的把它測出來。

不過最上方的「探索卡」、「大挑戰」仍然可以使用。

這讓我發現:

Loading 不代表整個 App 都必須鎖住,只需要限制會影響這次 Gemini Request 的操作。

Day22 testB new cards loading button cannot click


Test C|故意讓 Gemini API 失敗

正常流程確認完後,我想知道 Error Handling 到底是不是真的有用。

所以我暫時把 Server 使用的模型改成一個不存在的名稱:

const candidateModels = ['gemini-day22-error-test'];

Day22 testC add wrong gemini model

接著送出:

學習對象:7~8 歲兒童
學習主題:人體器官
卡片數量:5

Day22 TestC 7-8 body 5cards

這次流程變成:

Submit
→ Loading
→ Gemini API
→ 404 NOT_FOUND
→ Error

Console 收到:

models/gemini-day22-error-test is not found
status: NOT_FOUND

但 App 沒有 Crash,Loading 也正常結束,畫面成功進入 Error State:

學習卡產生失敗

代表至少:

Gemini API 失敗時,前端確實有把錯誤接住。

Day22 TestC error message


API 失敗後,原本的卡片還在嗎?

這次我也特別回到 Learning Card 畫面確認。

剛才 Test A 成功產生的:

交通工具 3 / 3

仍然存在,原本的「飛機」卡片也可以正常顯示。

Day22 TestC error after testA transporation cards still vaild

所以目前的資料更新方式可以理解成:

Gemini Success
→ 更新 LearningCard[]
→ 更新 React State

Gemini Error
→ 顯示 Error
→ 保留原本 React State

也就是說,一次 API Failure 不會把上一組成功產生的教材一起清空。

對實際使用來說,這個行為合理很多。


實測後還是發現一個問題

Gemini 3.8 Flash 原本表示已經將錯誤轉成比較友善的訊息。

但 Test C 實際遇到 404 NOT_FOUND 時,畫面仍然出現了一大段 Gemini API 原始訊息:

models/gemini-day22-error-test is not found...
status: NOT_FOUND

回頭看程式才發現,目前有針對網路問題、503、API Key 等情況處理,但沒有特別處理這次的 404。

所以目前比較準確的狀態是:

Error Handling 有了,但 Error UX 還有改善空間。

這也再次證明,AI 告訴我「功能完成」,跟實際把 Error Case 跑過一次,還是兩件不同的事情。


今天完成了什麼?

Day 22 沒有增加新的 AI 功能,而是讓原本的 Gemini API 流程更完整。

目前整條流程已經變成:

Form Data
→ Prompt Template
→ Gemini API
        │
        ├─ Success
        │    ↓
        │ Structured Output
        │    ↓
        │ LearningCard[]
        │    ↓
        │ React State
        │    ↓
        │ Learning Card UI
        │
        └─ Error
             ↓
          Error State
             ↓
          保留原本 Cards

從 Day 18 第一次把 Gemini API 接進 App,到現在已經不只是:

API 有回應就算成功。

而是開始處理真正使用 AI App 時一定會遇到的:

等待、重複操作、API Failure,以及既有資料保留。


今天最大的收穫

今天最有感的其實不是新增多少程式碼,而是:

不要只測 AI 成功的時候。

如果今天只跑 Test A,我可能會覺得 Loading / Error Handling 已經沒有問題。

但真的故意讓 Gemini API 失敗一次後,才發現:

Error State       ✅
App 沒有 Crash    ✅
原本 Cards 保留   ✅
Error Message     ⚠️

這種問題只看 AI 產生的程式碼,其實不一定看得出來。

還是得真的跑一次。


明天預告

現在 Learning Card 已經可以:

輸入條件
→ Gemini 產生內容
→ Structured Output
→ LearningCard[]
→ 顯示在 UI

Loading 與 Error 也開始有基本的處理。

但還有一個很常見的情況:

Gemini 成功產生了,但這組內容我不喜歡怎麼辦?

目前只能重新回到表單再產生一次。

所以明天要來處理「重新產生」這件事,讓使用者不需要重新設定所有條件,就能請 Gemini 再生成另一組內容。

Day 23|生成一次還不夠:加入重新產生 Learning Card 功能


上一篇
Day 21|JSON 回來了,React 怎麼接?把 Gemini Response 變成 LearningCard[]
系列文
今天學什麼?30 天用 Google AI 打造智慧學習卡 共 22 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言