iT邦幫忙

2023 iThome 鐵人賽

DAY 22
0
Modern Web

職缺資訊平台—Jobscanner系列 第 22

[開發] React 從 0 到 0.1 (6)

  • 分享至 

  • xImage
  •  

設計 UI 互動的時候,會思考當使用者進行各項操作的時候, UI 該怎麼改變:

  • 填寫表單時,提交按鈕是 enabled 狀態
  • 點擊提交後,表單和按鈕都會變成 disabled 狀態,而且出現 spinner,提示使用者等待
  • 如果送出的請求成功,表單會隱藏,並且顯示 "Thank you" 訊息在畫面上
  • 如果送出的請求失敗,會顯示錯誤訊息,表單會重新 enabled

必須考慮多種情境,透過 UI 的變化來反映多種狀況


利用 state 反應各種 input 的變法,作法大致如下:

  1. 定義元件各種不同的 UI 狀態
  2. 決定什麽情況會觸發這些改變
  3. 用 useState 來呈現這些的 UI 狀態
  4. 移除不必要的狀態變數
  5. 透過 event handler 來設定 UI 狀態

定義元件各種不同的 UI 狀態?
例如,使用者填寫表單一連串的過程中,UI 元件的狀態:

  • Empty:當表單未填寫時,提交按鈕為 disabled
  • Typing:填寫時,提交按鈕為 enabled
  • Submitting:提交後,整份表單為 disabled,並顯示 Spinner
  • Success:顯示 "成功" 訊息在頁面上
  • Error:和 Typing 一樣,但額外顯示錯誤訊息

什麽情況會觸發這些改變?
可以透過兩類 input 事件觸發狀態的改變

  • 使用者:點擊按鈕、欄位輸入內容
  • 瀏覽器:收到 response、timeout、圖片載入

當這些 input 發生時,改變 state 變數


useState 來呈現這些的 UI 狀態?

當使用 useState 來代表不同 UI 狀態時,保持越簡單越好,每一組 state 變數都是可以改變的,組數越多越複雜也越難維護。


移除不必要的狀態變數?

在使用 state 變數時,有幾點可以注意:

  • state 有沒有互相矛盾的,例如:使用isTypingisSubmitting,但這兩個值不會同時為 true
  • 有沒有不同的 state 變數,卻代表著一樣的資訊

怎麼選擇適合的 state 資料結構 ?

  1. 將相關的 state 集合起來
    如果多個 state 變數會同時改變,可以合併成同一個 state 變數

    const [x, setX] = useState(0);
    const [y, setY] = useState(0);
    
    // 可以改寫成
    const [position, setPosition] = useState({ x: 0, y: 0 });
    
  2. 避免 state 之間的矛盾
    例如 isSendingisSent 絕對不會同時為 true,可用 status 來儲存三種狀態,typing、sending、sent

    const [isSending, setIsSending] = useState(false);
    const [isSent, setIsSent] = useState(false);
    
    // 可以改寫成
    const [status, setStatus] = useState('typing');
    
  3. 避免多餘的 state
    假設在執行過程中,已經從元件的 props 或是從 state 變數計算得到某些資訊,就不用再把這樣的結果存進 state 變數中

    // fullName 是從 firstName 和 lastName 得到的
    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    const [fullName, setFullName] = useState('');
    
    // 可以改寫成
    const [firstName, setFirstName] = useState('');
    const [lastName, setLastName] = useState('');
    
    const fullName = firstName + ' ' + lastName;
    
  4. 避免 state 變數資料的重複
    同一份資料在不同的 state 變數中,很難保持資料一致,避免複製資料內容

    // selectedItem 和 items 中項目內容是一樣的
    const [items, setItems] = useState(initialItems);
    const [selectedItem, setSelectedItem] = useState(
        items[0]
    );
    
    // 可以改寫成
    const [items, setItems] = useState(initialItems);
    const [selectedId, setSelectedId] = useState(0);
    
  5. 避免巢狀結構太深的 state 變數
    太多層的結構,會越難更新資料

如果要在多個元件之間共享 state,可將 state 移至最接近的父元件中,透過 prop 傳遞給子元件,將控制權交給外部父元件,保持每個狀態的 single source of truth。


React 也是利用樹狀結構來建置管理 UI,透過 JSX 建立 UI tree,接著 React DOM 再去更新對應位置的 DOM 元素

state 實際上是 React 透過其元件在 UI tree 中的位置而定,而不是真的儲存在元件內部,所以 state 認的是位置而不是所屬元件。

這兩個 counter 是因為在 tree 中的位置不同,所以被視為兩個不同的獨立元件

export default function App() {
  const counter = <Counter />;
  return (
    <div>
      {counter}
      {counter}
    </div>
  );
}

同一個元件在同一個 UI treee 位置,React 就會保留元件,也包含其狀態,如果是不同的元件,在同一個位置,state 則會被重新設定。

如果希望每次 re-render 時,state 可以保留,要記得每一次的 render UI tree 的結構要一致。如果結構不同,React 會從 tree 中移除元件,也一併移除 state。


如果"視覺上"元件要在同一個位置,卻又想要重設 state,可透過以下兩種方式:

  1. render 兩個元件在不同的位置
  2. 比較常見作法是,給一個識別的 key,讓 React 可以分辨元件。
    React 預設會利用元件在其父層的順序做辨識,但透過 key 來指定元件給 React,React 可以分別這是兩個不同的元件,也就不會使用到其他元件的 state。

上一篇
[開發] React 從 0 到 0.1 (5)
下一篇
[開發] React 從 0 到 0.1 (7)
系列文
職缺資訊平台—Jobscanner31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言