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.模擬非同步

我們藉由一個 PromisesetTimeout 模擬請求伺服器的 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) }}
直播中

尚未有邦友留言

立即登入留言