iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 19
0
Modern Web

關於React,那些我不知道的系列 第 19 篇

狀態管理的救星?! Recoil 來自遠方的狀態,如何處理Async State?

  • 分享至 

  • xImage
  •  

codesandbox demo

前面幾天的介紹,希望大家對 Recoil 的使用,有些基本的認識了。如果有說明不清楚、錯誤的地方,還請留言跟我說,我會儘速調整、修正。

今天會跟大家分享非同步狀態如何與 Recoil 結合。

情境

使用者可以透過瀏覽器切換 user id,但需要透過 user id 向伺服器請求 user 其他相關的資訊,我們希望這些 user 的狀態能在我們的 App 當中,供所有元件共享。

1.模擬非同步

我們藉由一個 Promise 配 setTimeout 模擬請求伺服器的 async request ,透過帶參數user id給Promise function,我們可以取回對應的 user object。

function getData(id) {
  const data = [
    {
      id: 0,
      name: "Lala"
    },
        ...
    {
      id: 5,
      name: "stan"
    }
  ];
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(data[id]);
    }, 500);
  });
}

2.建立User ID 的共享狀態 (atom) 與 切換 User ID 的元件。

假設我們預設一開始拿到的 userID 是 1 , 我們透過 Recoil API atom,將 userID 存入我們的 Recoil 。

export const currentUserIDState = atom({
  key: "CurrentUserID",
  default: 1
});

建立一組 Select 的元件,使我們能切換 user ID ,透過 useRecoilState 帶入需要使用的 shared state ,在這裡也就是我們的 currentUserIDState ,如此便能取得這個 state 與其 setter function。

function CurrentUserSelect() {
  const [userID, setUserID] = useRecoilState(currentUserIDState);

  const handleChangeUser = ({ target: { value } }) => {
    setUserID(value);
  };

  return (
    <>
      <span>切換User ID: </span>
      <select value={userID} onChange={handleChangeUser}>
        <option value={1}>user id 1</option>
        <option value={2}>user id 2</option>
        <option value={3}>user id 3</option>
        <option value={4}>user id 4</option>
        <option value={5}>user id 5</option>
      </select>
    </>
  );
}

3.透過 User ID 請求 User 其他的資訊,並且存入 Recoil

昨天我們透過 selector 裡面的 get function 取得 Recoil 內部的某個狀態,並計算出其他狀態,回傳存入 Recoil,這次也同樣是藉由 selector 取得當前的 userID ,並向伺服器發出請求,獲取 User Object 然後存入 Recoil。

在步驟一當中,我們定義了 user object 是 {id:0,name:'Ken'}的形式。如今我們需要取得這個 user object 後,將這個 user name 存入 Recoil。

export const currentUserNameState = selector({
  key: "CurrentUserName",
  get: async ({ get }) => {
    const user = await getData(get(currentUserIDState));
    return user.name;
  }
});

昨天的 get 屬性是放入一個同步的方法,今天我們在這方法前面加入 async ,使他成為非同步的方法。
其中我們向伺服器請求資料 (* 在這裡就是 getData(id) *)
codesandbox demo

getData(id)這個 id 從何而來呢?就是使用 get(currentUserIDState) 獲得Recoil當前的 user ID。
並且在這selector 儲存使用者姓名,因此我們 return user.name

現在我們會先看到編譯報錯說我們需要使用 <Suspense> ,因為我們向遠端發送請求時,我們不希望非同步的請求阻斷了 React 的渲染,因請透過 <Suspense> 通知 React ,並且定義了這段時間我們希望呈現的 Loading 畫面。

因此在我們的 App 加上 Suspense

        <Suspense fallback={<h1>loading......</h1>}>
          <CurrentUserSelect />
          <CurrentUserInfo />
        </Suspense>

最後,我們就能使用在元件裡調用我們的 user name

function CurrentUserInfo() {
  const userName = useRecoilValue(currentUserNameState);
  return <div>User name :{userName} </div>;
}

codesandbox demo

總結

  1. 透過 Selector 可以將非同步狀態儲存進入 Recoil
  2. 非同步的狀態,要搭配 <Suspense> 使用,若想要更客製化 Loading 可使用 useRecoilValueLoadable API (之後分享)
  3. Recoil 會將非同步取得回來得狀態做 cache 以便提升使用者體驗 (可以玩看看demo,切換不同狀態,再回到某個已請求過得狀態)

上一篇
狀態管理的救星?! Recoil 如何開始?(3) Selector
下一篇
狀態管理的救星?! Recoil 想用參數來切換 state 怎麼辦? ( selectorFamily )
系列文
關於React,那些我不知道的 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言