Describing the UI: Unveiling the Power of React Components

picture

2024-03-29

Describing the UI: Unveiling the Power of React Components

描述使用者介面:揭示 React 元件的威力

前言

這篇文章是筆者在學習 React 時,閱讀 React 官方文件 所做的翻譯筆記,希望能幫助更多的人學習 React。(React 官方文件的翻譯筆記將會是一個系列,敬請期待!)

這個章節主要是描述使用者介面,讓你了解如何建立、自定義和有條件地顯示 React 元件等。

這是一個類似於大綱的章節,不會提供過多細節,每個標題底下頂多提供一個簡單的範例,這個章節的目的,是讓初學者對使用者介面有個基本且快速的了解。

本篇文章沒有節錄原文範例的輸出結果,如有需要請至原文對照參考。

每個標題都有延伸的章節可以閱讀,之後筆者會再提供逐篇翻譯。延伸章節有提供實作的部分,建議可以參考原文練習。

Describing the UI (描述使用者介面)

React 是一個用於呈現使用者介面(UI)的 JavaScript 函式庫。UI 是由像按鈕、文字和圖像這樣的小單位所建構而成。

React 讓你將它們結合成可重複使用、可嵌套的元件 。從網站到手機應用程式,螢幕上的所有內容都可以拆分為元件。

在這一章節中,你將學習建立、自定義和有條件地顯示 React 元件。

目錄

Your first component (你的第一個元件)

React 應用程式由稱為元件的獨立 UI 塊組成。

一個 React 元件是一個你可以在其中加入標記文本的 JavaScript 函式。

元件可以小至一個按鈕,大至整個頁面。

以下是一個名為 Gallery 的元件,呈現了三個 Profile 元件:

App.js :

function Profile() {
  return <img src='https://i.imgur.com/MK3eW3As.jpg' alt='Katherine Johnson' >;
}

export default function Gallery() {
  return (
    <section>
      <h1>Amazing scientists</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

延伸閱讀

閱讀 你的第一個元件 來學習如何宣告和使用 React 元件。

Importing and exporting components (匯入與匯出元件)

你可以在一個檔案中宣告多個元件,但大型檔案可能會讓導覽變得困難。

為了解決這個問題,你可以將一個元件 匯出(export) 到它自己的檔案中,然後從另一個檔案 匯入(import) 該元件:

Gallery.js :

import Profile from "./Profile.js";

export default function Gallery() {
  return (
    <section>
      <h1>Amazing scientists</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

Profile.js :

export default function Profile() {
  return <img src='https://i.imgur.com/QIrZWGIs.jpg' alt='Alan L. Hart' >;
}

延伸閱讀

閱讀 匯入與匯出元件 以學習如何將元件分割為它們自己的檔案。

Writing markup with JSX (使用 JSX 撰寫標記文本)

每個 React 元件都是一個 JavaScript 函式,其中可能包含一些標記文本(markup),React 會將其呈現到瀏覽器中。

markup:
指的是標記語言中的標記文本,用於定義文檔的結構和格式。
HTML 是由標籤(tag)組合而成的標記文本,其中的 <div><p><span> 就是標籤(tag)。
在 React 中,JSX 也是一種類似標記文本的語法,用於定義 UI 元素的結構。

tag VS markup :
我們常常看到 tag 與 markup 都被翻譯為「標籤」,筆者認為他們的差異在於:tag 指的是 HTML 的標籤,而 markup 指的是標記語言的標記。
像是 <h1> 是一個 tag,而 <h1>標題</h1> 則是一個 markup。
實際上,HTML 就是 HyperText Markup Language (超文本標記語言) 的縮寫。
接下來,筆者會將 tag 譯為「標籤」,而 markup 譯為「標記文本」,以此區別兩者。

React 元件使用一種語法擴展叫做 JSX 來表示該標記文本。JSX 看起來很像 HTML,但它更嚴格,並且可以顯示動態資訊。

如果我們將現有的 HTML 標記文本(HTML markup)貼到 React 元件中,它不一定會正常運作:

App.js :

export default function TodoList() {
  return (
    // This doesn't quite work!
    <h1>Hedy Lamarr's Todos</h1>
    <img
      src="https://i.imgur.com/yXOvdOSs.jpg"
      alt="Hedy Lamarr"
      class="photo"
    >
    <ul>
      <li>Invent new traffic lights
      <li>Rehearse a movie scene
      <li>Improve spectrum technology
    </ul>
  );
}

Error :

/src/App.js: Adjacent JSX elements must be wrapped in an enclosing tag. Did you want a JSX fragment <>...</>? (5:4)

  3 |     // This doesn't quite work!
  4 |     <h1>Hedy Lamarr's Todos</h1>
> 5 |     <img
    |     ^
  6 |       src="https://i.imgur.com/yXOvdOSs.jpg"
  7 |       alt="Hedy Lamarr"
  8 |       class="photo"

如果你有類似這樣的現有 HTML,你可以使用轉換器來修復它:

export default function TodoList() {
  return (
    <>
      <h1>Hedy Lamarr's Todos</h1>
      <img src='https://i.imgur.com/yXOvdOSs.jpg' alt='Hedy Lamarr' className='photo' >
      <ul>
        <li>Invent new traffic lights</li>
        <li>Rehearse a movie scene</li>
        <li>Improve spectrum technology</li>
      </ul>
    </>
  );
}

延伸閱讀

閱讀 使用 JSX 撰寫標記文本 以學習如何撰寫有效的 JSX。

JavaScript in JSX with curly braces (JSX 中使用花括號表示 JavaScript)

JSX 允許你在 JavaScript 檔案中寫入類似 HTML 的標記文本,將渲染邏輯和內容放在同一個地方。

有時你會希望在該標記文本中添加一些 JavaScript 邏輯或引用動態屬性。

在這種情況下,你可以在 JSX 中使用花括號來“打開一個窗口”到 JavaScript:

const person = {
  name: "Gregorio Y. Zara",
  theme: {
    backgroundColor: "black",
    color: "pink",
  },
};

export default function TodoList() {
  return (
    <div style={person.theme}>
      <h1>{person.name}'s Todos</h1>
      <img className='avatar' src='https://i.imgur.com/7vQD0fPs.jpg' alt='Gregorio Y. Zara' >
      <ul>
        <li>Improve the videophone</li>
        <li>Prepare aeronautics lectures</li>
        <li>Work on the alcohol-fuelled engine</li>
      </ul>
    </div>
  );
}

延伸閱讀

閱讀 JSX 中的 JavaScript 與花括號 以學習如何從 JSX 中訪問 JavaScript 資料。

Passing props to a component (將 props 傳遞給元件)

React 元件使用 props 來彼此通訊。

每個父元件都可以通過將 props 傳遞給其子元件來傳遞一些資訊。

Props 可能讓你想到了 HTML 屬性,但你可以通過它們傳遞任何 JavaScript 值,包括物件、陣列、函式,甚至是 JSX!

App.js :

import { getImageUrl } from "./utils.js";

export default function Profile() {
  return (
    <Card>
      <Avatar
        size={100}
        person={{
          name: "Katsuko Saruhashi",
          imageId: "YfeOqp2",
        }}
      />
    </Card>
  );
}

function Avatar({ person, size }) {
  return <img className='avatar' src={getImageUrl(person)} alt={person.name} width={size} height={size} >;
}

function Card({ children }) {
  return <div className='card'>{children}</div>;
}

utils.js :

export function getImageUrl(person, size = "s") {
  return "https://i.imgur.com/" + person.imageId + size + ".jpg";
}

延伸閱讀

閱讀 將 Props 傳遞給元件 以學習如何傳遞和讀取 props。

Conditional rendering (條件渲染)

你的元件通常會根據不同的條件顯示不同的內容。在 React 中,你可以使用 JavaScript 語法,如 if 語句、 &&? : 運算子(operator)來有條件地渲染 JSX。

在這個例子中,使用了 JavaScript 的 && 運算子來有條件地渲染一個勾勾符號(checkmark):

function Item({ name, isPacked }) {
  return (
    <li className='item'>
      {name} {isPacked && "✔"}
    </li>
  );
}

export default function PackingList() {
  return (
    <section>
      <h1>Sally Ride's Packing List</h1>
      <ul>
        <Item isPacked={true} name='Space suit' />
        <Item isPacked={true} name='Helmet with a golden leaf' />
        <Item isPacked={false} name='Photo of Tam' />
      </ul>
    </section>
  );
}

延伸閱讀

閱讀 條件渲染 以學習不同的條件渲染內容的方式。

Rendering lists (渲染清單)

你通常會想要從一組資料中顯示多個相似的元件。

你可以在 React 中使用 JavaScript 的 filter()map() 來過濾和轉換你的資料陣列為元件陣列。

對於每個陣列項目,你需要指定一個 key

通常,你會希望使用來自資料庫的 ID 作為 keykey 讓 React 能夠跟蹤列表中每個項目的位置,即使列表發生變化。

App.js :

import { people } from "./data.js";
import { getImageUrl } from "./utils.js";

export default function List() {
  const listItems = people.map((person) => (
    <li key={person.id}>
      <img src={getImageUrl(person)} alt={person.name} >
      <p>
        <b>{person.name}:</b>
        {" " + person.profession + " "}
        known for {person.accomplishment}
      </p>
    </li>
  ));
  return (
    <article>
      <h1>Scientists</h1>
      <ul>{listItems}</ul>
    </article>
  );
}

data.js :

export const people = [
  {
    id: 0,
    name: "Creola Katherine Johnson",
    profession: "mathematician",
    accomplishment: "spaceflight calculations",
    imageId: "MK3eW3A",
  },
  {
    id: 1,
    name: "Mario José Molina-Pasquel Henríquez",
    profession: "chemist",
    accomplishment: "discovery of Arctic ozone hole",
    imageId: "mynHUSa",
  },
  {
    id: 2,
    name: "Mohammad Abdus Salam",
    profession: "physicist",
    accomplishment: "electromagnetism theory",
    imageId: "bE7W1ji",
  },
  {
    id: 3,
    name: "Percy Lavon Julian",
    profession: "chemist",
    accomplishment: "pioneering cortisone drugs, steroids and birth control pills",
    imageId: "IOjWm71",
  },
  {
    id: 4,
    name: "Subrahmanyan Chandrasekhar",
    profession: "astrophysicist",
    accomplishment: "white dwarf star mass calculations",
    imageId: "lrWQx8l",
  },
];

utils.js :

export function getImageUrl(person) {
  return "https://i.imgur.com/" + person.imageId + "s.jpg";
}

延伸閱讀

閱讀 渲染清單 以學習如何渲染一個元件清單,以及如何選擇一個鍵(key)。

Keeping components pure (保持元件的純函式特性)

某些 JavaScript 函式是 純函式 。一個純函式具有以下特性:

  • 只管自己的事。 它不會改變在它被呼叫之前存在的任何物件或變數。
  • 同樣的輸入,同樣的輸出。 給定相同的輸入(inputs),一個純函式應該要始終都回傳相同的結果(result)。

嚴格地只將你的元件編寫為純函式,你就可以避免在程式碼庫增長時,遇到的一整類令人困惑的錯誤和不可預測的行為。以下是一個不純的元件的例子:

App.js :

let guest = 0;

function Cup() {
  // Bad: changing a preexisting variable!
  guest = guest + 1;
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup />
      <Cup />
      <Cup />
    </>
  );
}

你可以通過傳遞一個 prop,而不是修改一個預先存在的變數,來使這個元件成為純函式(make this component pure):

App.js :

function Cup({ guest }) {
  return <h2>Tea cup for guest #{guest}</h2>;
}

export default function TeaSet() {
  return (
    <>
      <Cup guest={1} />
      <Cup guest={2} />
      <Cup guest={3} />
    </>
  );
}

延伸閱讀

閱讀 保持元件的純函式特性 以學習如何將元件編寫為純函式,以獲得可預測性。

Your UI as a tree (將你的 UI 視為樹狀結構)

React 使用樹狀結構來模擬(model)元件和模組之間的關係。

model: 表示建模或模擬的行為。

React render tree

一個 React 渲染樹(React render tree)是對元件之間父子關係的表示。

An example React render tree:

generic_render_tree

靠近樹的頂部、根元件附近的元件被認為是頂級元件(top-level components)。

沒有子元件的元件被稱為葉子元件(leaf components)。

這種元件的分類對於理解資料流(data flow)和渲染效能(rendering performance)非常有用。

module dependency tree

對於理解你的應用程式,模擬 JavaScript 模組之間的關係是另一種有用的方式。我們稱之為模組依賴樹(module dependency tree)。

An example module dependency tree:

generic_dependency_tree

依賴樹通常被建構工具(build tools)用來將所有相關的 JavaScript 程式碼打包(bundle),供客戶端下載和渲染(download and render)。

對於 React 應用程式而言,較大的打包尺寸會降低使用者體驗。了解模組依賴樹有助於解決此類問題。

延伸閱讀

閱讀 將你的 UI 視為樹狀結構 以學習如何為 React 應用程式創建渲染和模組依賴樹,以及它們如何作為改善使用者體驗和性能的有用心智模型。

結論

此章節簡易的介紹了 React 的基本概念,讓你了解如何建立、自定義和有條件地顯示 React 元件等。這篇文章只是初淺的概念而已,如果想要更深入的了解,建議可以閱讀延伸閱讀的章節。(或者等筆者發布新的翻譯筆記?)

剛開始學習一門新的程式語言時,不曉得各位讀者傾向於哪種學習方式呢?

有些人喜歡閱讀官方文件、有些人喜歡看影片教學、有些人喜歡參考網路文章等。不管是哪種方式,只要能夠讓自己理解並且掌握,都是好的學習方式。(當然,最好的學習方式是多方面的學習,也就是「我全都要」。)

筆者喜歡的學習方法是一邊看教學影片、一邊實作,再閱讀官方文件,把相關的概念串起來。雖然嗑英文的時間比較久,但官方文件是最正確的資訊,所以不怕嗑錯?(笑)

本文參考來源

Describing the UI

所有章節的延伸閱讀清單

liz_avatar

Liz

軟體工程師

喜歡冷笑話和蒐集迷因。有點社交焦慮。在 MBTI 中連續多年的測試結果為 ISTP。最愛的活動是坐在沙發上和好朋友玩電動一整天。

查看作者的其他文章

分享到

回上頁