iT邦幫忙

2023 iThome 鐵人賽

DAY 10
0

今天要來學習在設計元件時有哪些要點,以及如何避免寫出「不乾淨」的元件。

純淨的元件

製作元件時的大重點:保持元件的純淨 (Keeping Components Pure)

具體來說是什麼呢?你可以想像元件就像是食譜,只要照著食譜指示的原料以及步驟,就可以製作成固定、特定口味的料理。所以我們可以理解元件的產出必須要有穩定性,而不會因為每一次的製作有出乎意料的結果。

理解了元件的特質,我們可以遵循一些原則來讓元件是個好食譜。

  • 讓元件專注於自己該做的事

    假如有一個元件,它被用來計算一個數字的平方。按照「專注於自己的事情」的原則,這個元件只應該專注於計算平方,而不應該在計算平方的過程中修改或影響其他已存在的變數或物件。

  • 元件的渲染結果應該具備可預測性:也就是相同的input則應有相同的產出

  • 元件應該只負責回傳 JSX 元素,不應該在渲染之前修改任何之前存在的物件或變數。元件應該只依賴於傳入的屬性(props),並將所需的資料作為 props 進行處理和渲染。

練習

實際來看看一些React官方文件的challeges 練習來理解吧。

(1) 修時鐘

這是一個時鐘,它應該在特定的時段切換白天與夜晚,但時鐘無法正常顯示,為什麼?

export default function Clock({ time }) {
  let hours = time.getHours();
  if (hours >= 0 && hours <= 6) {
    document.getElementById('time').className = 'night';
  } else {
    document.getElementById('time').className = 'day';
  }
  return (
    <h1 id="time">
      {time.toLocaleTimeString()}
    </h1>
  );
}
  • 問題點:在時鐘元件裡,「元件」應該專注於自身,不應該做其他的事情。它做了 document.getElementById('time') 。
  • 思考:
    • 時鐘應該做什麼?時鐘應該只做取得時間並依據時間切換class

    • 真正要動的東西是什麼? 時間以及<h1>的class → 也就是在return的JSX挖二個JavaScript花括號讓我們所需要的資料能夠動態地出現。

        <h1 className={className}>
            {time.toLocaleTimeString()}
        </h1>
      
    • 邏輯寫什麼?洞挖好了再來把邏輯補上,維持原先的if statement ,也回憶起JSX的class寫法了。done。

解決方法:

export default function Clock({ time }) {
  let hours = time.getHours();
  let className;
  if (hours >= 0 && hours <= 6) {
    className = 'night';
  } else {
    className = 'day';
  }
  return (
    <h1 className={className}>
      {time.toLocaleTimeString()}
    </h1>
  );
}

(2) 一個壞掉的profile

這是一個Profile元件,它已經壞掉了。當我們點擊 Collapse收合再打開,它會呈現同一人物。

上程式碼:

import Panel from './Panel.js';
import { getImageUrl } from './utils.js';

let currentPerson;

export default function Profile({ person }) {
  currentPerson = person;
  return (
    <Panel>
      <Header />
      <Avatar />
    </Panel>
  )
}

function Header() {
  return <h1>{currentPerson.name}</h1>;
}

function Avatar() {
  return (
    <img
      className="avatar"
      src={getImageUrl(currentPerson)}
      alt={currentPerson.name}
      width={50}
      height={50}
    />
  );
}

點擊前:

點擊後:

  • 問題點:the current person在渲染的過程中改變了,因為存在著預先存在的變數(preexisting variable)也可以視為全域變數,而Profile 元件寫入了該變數。原本**currentPerson** 被放在全域範圍中,並在 Profile 元件以及 HeaderAvatar 元件中共用,這導致結果無法預測。

  • 思考:

    • 如何重新組織元件們與變數的關係?

    我們先看看 問題的程式碼的資料結構關係:

    • Profile 元件將 currentPerson 設定為 person
    • HeaderAvatar 函數直接訪問全域變數 currentPerson

    那三個元件彼此應該要是什麼關係?Profile 要包含 HeaderAvatar,而這在問題的程式碼中,則沒有因爲依據這樣的關係來傳遞資料。

    明白了這階層關係,我們可以運用這樣的關係來設計資料的傳遞,也透過最大的元件Profile來設定prop 並將資料傳遞給子元件。

解決方法:

import Panel from './Panel.js';
import { getImageUrl } from './utils.js';

export default function Profile({ person }) {
  return (
    <Panel>
      <Header person={person} />
      <Avatar person={person} />
    </Panel>
  )
}

function Header({ person }) {
  return <h1>{person.name}</h1>;
}

function Avatar({ person }) {
  return (
    <img
      className="avatar"
      src={getImageUrl(person)}
      alt={person.name}
      width={50}
      height={50}
    />
  );
}

Profile 元件將 person 傳遞給 HeaderAvatar 元件作為屬性。

從上面二個練習可以看出若元件沒有保持純粹,則有可能沒辦法正常運作,也可能產生非預期的效果。

參考資料

  1. React 官方文件 - Keeping Components Pure

上一篇
Day 9 - key
下一篇
Day 11 - 在React處理事件(上)
系列文
30 days of React 30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言