Recoil - Facebook 官方 React 狀態管理器

說到狀態管理器,輪子滿天飛。在 Class 時代,redux 與 mobx 幾乎佔據了所有市場,幾乎沒有沒用過 redux 的同窗。隨着 Hooks 的誕生,新的一批輪子應運而生,其中有表明性的有 unstated-next、constate 等等。
固然不管什麼輪子,要解決的問題都是同樣的:**跨組件狀態共享。**在解決這個核心問題的同時,須要儘量的知足如下幾個特性:前端

  • TypeScript 支持
  • 友好的異步支持
  • 支持狀態互相依賴
  • 同時支持 Class 與 Hooks 組件
  • 使用簡單

Recoil 體驗

最近,facebook 官方出了一個狀態管理器解決方案 Recoil,咱們來體驗一下。 react

準備工做

使用 Recoil,咱們須要在項目最外層包一個 RecoilRoot ,這個和大部分狀態管理器一致,經過 context 來跨組件傳遞數據。git

import React from 'react';
import { RecoilRoot } from 'recoil';

function App() {
  return (
    <RecoilRoot>
        ...
    </RecoilRoot>
  );
}
複製代碼

跨組件狀態共享

狀態最簡單的就是定義和使用。在 Recoil 中,經過 atom 來定義一個狀態。github

const inputValueState = atom({
  key: "inputValue",
  default: ""
});
複製代碼

如上面的代碼所示,咱們定義了一個 inputValue 狀態,它的默認值是空字符串。
須要注意的是 key 字段,它應該是全局惟一的。這個 key 主要爲了 debug 方便,持久化數據(數據恢復時的惟一標識),以及能夠方便的看到全局 atoms 樹。

消費狀態也比較簡單,經過 useRecoilState 來消費狀態。typescript

import React from "react";
import { useRecoilState } from "recoil";
import { inputValue } from "../store";

const InputA = () => {
  const [value, setValue] = useRecoilState(inputValueState);

  return <input value={value} onChange={e => setValue(e.target.value)} />;
};

export default InputA;
複製代碼

是否是很簡單?Recoil 的基礎用法就是這樣的。我在這裏寫了一個 demo,你能夠體驗下。
redux

2020-05-17 13.09.49.gif

狀態互相依賴

有些狀態須要依賴其它狀態,這時候就要用 selector 來定義這個狀態了。
好比,咱們須要定義一個新的狀態 filterdInputValue ,它是過濾 inputValue 中的數字後的值。markdown

const filterdInputValue = selector({
  key: "filterdInputValue",
  get: ({get}) => {
    // 經過 get 能夠讀取其它狀態
    const inputValue = get(inputValueState);
    return inputValue.replace(/[0-9]/ig, "");
  },
});
複製代碼

selector 比較簡單,就是爲了實現狀態的依賴。你能夠在這個 demo 體驗下。
異步

2020-05-17 13.29.55.gif

異步支持

良好的異步請求支持是狀態管理器必不可少的。Recoil 提供了一個 useRecoilValueLoadable 來處理異步請求。直接上例子:async

const currentUserNameQuery = selector({
  key: "CurrentUserName",
  get: async () => {
    const response = await queryUserInfo();
    return response.name;
  }
});
複製代碼

咱們須要經過 selector 來定義異步狀態,若是 get 函數是一個 Promise,則表明該狀態爲異步狀態,須要使用 useRecoilValueLoadable 來消費該狀態。函數

const UserName = () => {
  const userNameLoadable = useRecoilValueLoadable(currentUserNameQuery);
  switch (userNameLoadable.state) {
    case "hasValue":
      return <div>{userNameLoadable.contents}</div>;
    case "loading":
      return <div>Loading...</div>;
    case "hasError":
      throw userNameLoadable.contents;
  }
};
複製代碼

從上面例子能夠看到, useRecoilValueLoadable 返回的狀態,能夠經過 state 字段讀取到異步請求的狀態。我寫了個 demo,你能夠體驗下。

2020-05-17 15.34.53.gif


固然經過 useRecoilValueLoadable 來消費異步狀態,比較符合咱們當前的習慣。但 Recoil 更推薦經過 React.Suspense 來消費異步狀態,這裏就仁者見仁了,雖然 Suspense 多是方向,但用起來是還不太習慣。

const UserName = () => {
  const userName = useRecoilValue(currentUserNameQuery);
  return <>{userName}</>
 }
};
function MyApp() {
  return (
    <React.Suspense fallback={<div>Loading...</div>}>
      <UserName />
    </React.Suspense>
  );
}
複製代碼

評價

優勢

  • 以前狀態管理器滿天飛,若是官方能一統天下,應該算一件好事情。
  • 對 React concurrent 模式支持良好。

不足

當前 Recoil 還處於開發階段,文檔都還不是很全。基於現狀,說幾點個人感覺。

1. 沒有使用 ts 實現,目前不支持 ts

這點我很驚訝,也是寫這個文章的時候才發現的,很奇怪。講道理 Recoil 支持 typescript 應該是順手的事情,可能後期須要來個 @types/recoil 吧。

2. 目前沒有支持 Class 組件消費狀態。

這個特性應該是必備的,應該不會完全拋棄 Class 組件。估計下個版本確定會支持的這個特性的。實現成本較低,不支持的話就太反人類了。

3. API 偏多,有必定上手成本。

image.png

各種 API 一共有 19 個,偏複雜了。感受不少都是能夠合併的,好比 atom 和 selector 合併成一個等等(也多是我考慮不成熟)。建議官方能夠考慮精簡精簡,原本是一個很簡單的東西,搞的太複雜了。

4. 消費較繁瑣

咱們須要消費一個狀態的時候,須要 import 兩個東西,比較繁瑣。

import { useRecoilState } from "recoil";
import { inputValueState } from "../store";

// 用法
useRecoilState(inputValueState);
複製代碼

原本應該能夠直接經過字符串 key 消費的,但這樣和 redux 問題同樣了,沒法支持 ts。

import { useRecoilState } from "recoil";

useRecoilState('inputValueState');
複製代碼

不管若是,import 兩個東西不是一個好的用法。

5. 沒有足夠的亮點

沒有看到讓人眼前一亮的東西,沒有使用衝動。靜觀發展~

後記

Recoil 總體看下來,比較中庸,須要靜觀發展。
另外推薦一下我目前正在用的最簡單的 React 狀態管理器 hox,只有一個 API,很是符合直覺,沒有任何上手成本,徹底擁抱 Hooks 😋。

感謝閱讀

關注公衆號「前端技術磚家」,拉你進交流羣,你們一塊兒共同交流和進步。

相關文章
相關標籤/搜索