【React筆記】在rendering lists常踩到的雷

React筆記、心得、踩雷經驗談

11/11/2024 | 江森 | 閱讀時間:6 min |
. . .
cover picture

前言

當前端開發者在嘗試建立頁面時,一定會碰上利用相似的一串資料,例如陣列,來連續渲染 重複結構的元素,到頁面的某個區塊上這種情況。

在 React 中,基於單一資料流原則中的資料不可變性(immutable),所以 React 的官方 文件建議開發者使用 JavaScript 原生的陣列操作方法:.filter()以及.map(),來操作資 料。

舉例來說,例如你拿到一包學生資料如下:

const students = [
  {
    id: 1,
    name: "John Doe",
    grade: 10,
    subjects: ["Math", "Science", "English"],
  },
  {
    id: 2,
    name: "Jane Smith",
    grade: 11,
    subjects: ["History", "Art", "Music"],
  },
  {
    id: 3,
    name: "Michael Johnson",
    grade: 12,
    subjects: ["Physics", "Chemistry", "Biology"],
  },
  {
    id: 4,
    name: "Emily Davis",
    grade: 9,
    subjects: ["Math", "English", "Spanish"],
  },
  {
    id: 5,
    name: "David Lee",
    grade: 11,
    subjects: ["Computer Science", "Math", "PE"],
  },
];

你打算用這串資料來建立學生列表,在 React 專案中就可以這麼做:


const StudentsList = () => {
  return (
    <ul>
      studuents.map(student => (<list key={student.id}>student.name</list>
      ))
    </ul>
  );
};

雷點

注意到上面 arrow function 的部分,我還在萌新階段,尚在摸索 React 的時候,曾對=> 後面直接接(),而不是{},感到疑惑。

後來才了解這是 arrow function 的特性: => 的後面如果只有一行時,可以不使用 return 關鍵字,而是用()來直接回傳內容。

再來就是,使用.map() 渲染重複結構的 UI 元件時,注意這樣連續的元件一定要設定 key。

否則渲染出來的 UI 元件可能會出現問題,因為 React 遇到像這種結構類似、連續大量重 複的元件時,是利用 key 來辨別它們,並以此來選擇要操作的資料。

我踩到的雷就是,我雖然有設定 key,但一開始是用陣列的 index 當作元件的 key。一開 始只有顯示畫面還沒有什麼問題,但是正如那句老話:所不出意外的話,馬上就要出意外了 。當我打算將這些元件修改成可以互動,例如 input 表單時,React 執行 re-render 就開 始出現異常。

表單設定了 onChange,但是輸入欄位每打一個字就會解除輸入 focus 狀態,這造成使用者 沒辦法正常輸入文字。找來找去,左思右想才發現是 key 的問題,由於陣列的 index 會因 為你修改陣列而產生變動(其實在 React 不能直接修改陣列,而是用新的取代舊的),前 面有提到 key 是 React 辨識 UI 的方式,當 key 會變動時,React 就不曉得你打算修改 的是哪個 UI,所以異常的出現了。

解決方式

所以我們需要將 UI 的 key 保持唯一不變,最好的方式是從後端拿到的資料時就自帶 id, 我們將 id 直接跟 key 綁定。但總會有例外的時候,這種情況,我們可以利用一個叫 uuid 的套件工具來解決。

首先是安裝:

npm install uuid

然後在你的 React 專案中引入:

import { v4 as uuidv4 } from "uuid";

再來就是在你要使用的地方,呼叫 uuidv4()來產生 id 即可,拿上面的學生列表來舉例:

const StudentsList = () => {
  const [studentInfo, setStudentInfo] = useState([]);

  //假設在某個函式中,處理拿到的API資料,並更新狀態管理hook
  setStudentInfo(
    studentDataFromAPI.map((stu) => ({
      id: stu.id,
      name: stu.name,
      grade: stu.grade,
      subjects: stu.subjects.map((item) => ({
        id: uuidv4(), //原先陣列中的元素變成物件,並利用uuid加入id
        subject: item,
      })),
    }))
  );

  return (
    <ul>
      studuentInfo.map((student) => (
      <list key={student.id}>
        <p>姓名:{student.name}</p>
        <p>年級:{student.grade}</p>
        <p>
          科目:
          {student.subjects.map((item) => (
            //加入唯一key,渲染後的行為就可以正常運作
            <span key={item.id}>item.subject</span>
          ))}
        </p>
      </list>
      ));
    </ul>
  );
};

小結

Rendering List 是在 React 開發上,一定會遭遇的概念,只要掌握住 React 運作的底層 邏輯,像是單一資料來源、資料不可變性(immutable),就可以少踩一點雷。

像這次我踩到 key 唯一性的雷,導致 UI 的行為出現問題,解決方式除了跟後端協調,幫 資料加上 id 外,前端也有這次介紹的工具 uuid 來解決這樣的問題。

在此留下這篇的踩雷紀錄給自己備忘,或是誤打誤撞來到這裡,有緣看到這篇的你。