Recoil - Facebook 官方 React 狀態管理器


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

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

Recoil 體驗

最近,facebook 官方出了一個狀態管理器解決方案 Recoil[1],咱們來體驗一下。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 來消費狀態。web

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[2],你能夠體驗下。

typescript

狀態互相依賴

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

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

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

異步支持

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

const currentUserNameQuery = selector({
  key: "CurrentUserName",
  getasync () => {
    const response = await queryUserInfo();
    return response.name;
  }
});

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

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[4],你能夠體驗下。


固然經過 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 偏多,有必定上手成本。


各種 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[5],只有一個 API,很是符合直覺,沒有任何上手成本,徹底擁抱 Hooks 😋。

Reference

[1]

Recoil: https://recoiljs.org/

[2]

demo: https://codesandbox.io/s/recoil-input-demo-7vly9?file=/src/components/InputA.js

[3]

demo: https://codesandbox.io/s/recoil-selector-demo-sn2kw?file=/src/App.js

[4]

demo: https://codesandbox.io/s/recoil-async-demo-qu6vw?file=/src/App.js

[5]

hox: https://github.com/umijs/hox


◆ ◆ ◆  ◆ 


學習交流

  • 關注公衆號【前端宇宙】,每日獲取好文推薦
  • 添加微信,入羣交流

「在看和轉發」 就是最大的支持


本文分享自微信公衆號 - 前端宇宙(gh_8184da923ced)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。

相關文章
相關標籤/搜索