React Next Update:


Suspense for Date Fetching
&
Concurrent Mode

 

(最後在分享個小東西:useSound 🎺)

Ideas : Putting Research into Production

  1. 過多的 loading state,會讓我們覺得頁面切換變慢了!
    • Concurrent Mode 提供 新的過場選擇(停留舊畫面)來避免這件事。
       
  2. Hover 或 text input 需要在短時間內被快速處理,但是 clicks 和 頁面切換則可以等久一點也不會覺得lag !
    • 我們心理上對於 Web 事件的不同期待,讓 React 決定實作「依照優先順序的render 」

在前端實作 Loading 和 排序資料顯示

Suspense  for Date Fetching

Traditional Approach 1

  • Render 後才去呼叫 API
  • 會因為一層一層的 Render,造成抓資料時的 Waterfall
    • 一個拉完後先顯示,再拉下一個在顯示剩下的。
  • Loading時間都一樣久,一個跑完還要再等下一個。
// In a function component:
useEffect(() => {
  fetchSomething();
}, []);

Traditional Approach 2

  • 使用 GraphQL 的 Fragment,這樣你才能在 Render 前就知道 Component 需要什麼資料。
    • 讓 Fragment 被 Compose 起來,就能避免抓資料時的 Waterfall。
  • 資料是一次同時拉,但需要等所有資料都拉好才會一次顯示,不會先顯示拉好的東西,使用者可能會需要等一段時間。

read() → 拉完之後才會去讀資料

前兩種的資料都會有為 null 的狀態

// Start fetching early!
const resource = fetchProfileData();

// ...

function ProfileDetails() {
  // Try to read user info
  const user = resource.user.read();
  return <h1>{user.name}</h1>;
}

Concurrent Mode

改善換頁體驗:換頁時暫留原本的畫面

先來介紹什麼是Transitions

Transition 就是指切換頁面的那個 Transition。


 

官方提供的範例來說

 Home Page 切到 Profile Page 時,

原本的畫面就不見了,只剩下一個大大的 Loading 😢。

The Three Steps of Transition

How to do that !?

  • 概念可用 Git branch 來理解。
    • startTransition 的 更新是在與現在這畫面不同的 branch
      • isPending 讓我們知道那個 新畫面的 branch 正在準備
      • 新畫面拉好後,我們現在的 branch 就會 merge 過去。
    • 實際上不是同時 render 兩個東西,而是快速切換成兩者。

在跳頁時,讓原本的畫面暫留一下子來跳過Loading State!

Origin

startTransition

Origin

🚨 isPending

Finished

NEW

useTransition 

  • startTransition

    • Function,可以用來告訴 React 哪些 State Update 可以延後生效

  • isPending

    • Boolean ,代表 Transition 是否正在進行。

      • 讓原先的頁面能顯示 Loading 提示,不然停在原本的頁面也會讓使用者以為網頁失去回應。

  • timeoutMs

    • Number,設定一個 Pending 的時間上限,超過了時間無論畫面有多糟都是直接進行 State Update ( Loading )。
       

  • No useTransition vs useTransition
  const [
    startTransition,
    isPending
  ] = useTransition({
    timeoutMs: 10000
  });

  function handleClick() {
    startTransition(() => {
      onClick();
    });
  }

  <>
    <button
      onClick={handleClick}
      disabled={isPending}
    >
      {children}
    </button>
    {isPending ? spinner : null}
  </>

Baking Transitions Into the Design System

Splitting High and Low Priority State

  • 我們還可以直接把 Loading 包進 Components 中 → Button
function handleChange(e) {
  const value = e.target.value;
  
  // Outside the transition (urgent)
  setQuery(value);

  startTransition(() => {
    // Inside the transition (may be delayed)
    setResource(fetchTranslation(value));
  });
}

直接包成hook

import { useDeferredValue } from 'react';

const deferredValue = useDeferredValue(
  value, { timeoutMs: 5000 // 延遲的毫秒數 });

Splitting High and Low Priority State

function handleChange(e) {
  const value = e.target.value;
  
  // Outside the transition (urgent)
  setQuery(value);

  startTransition(() => {
    // Inside the transition (may be delayed)
    setResource(fetchTranslation(value));
  });
}

Summary

  1. 改變前端讀取資料的方式
     

  2. 不只更改狀態,能更進一步去安排「更新的優先順序」
     

  3. 去除過多、冗長的 Loading 狀態
     

  4. 透過上述方式來 優化資料載入 和 頁面切換的體驗

功能對照:

(現在的版本已經變 Legacy Mode 了🥺)

外卡分享個有趣的小東西:useSound

1. Install

npm install use-sound

2. Finding and prepping sounds

3. Sound and accessibility

  • 不能干擾到 Screen readers(視力受損的人常會用)
    • Screen readers are pieces of software that parse the document and narrate its contents as sound.
  • Sites should remain 100% usable without sound.
    • 以防聽力受損的人可能會聽不到重要資訊。

How to use?

const [play, exposedData] = useSound('xxx.mp3', HookOptions);

exposedData

  • stop : function
     
  • pause : function
     
  • isPlaying : boolean
     
  • duratin : Number (milliseconds)

HookOptions

  • volume : 0 ~ 1
     
  • playbackRate : 0.5 ~ 4
    • slow down or speed up
       
  • interrupt : boolean

Demo

參考資料們:

useSound

By parkerhiphop

useSound

  • 311