說到狀態管理器,輪子滿天飛。在 Class 時代,redux 與 mobx 幾乎佔據了所有市場,幾乎沒有沒用過 redux 的同窗。隨着 Hooks 的誕生,新的一批輪子應運而生,其中有表明性的有 unstated-next、constate 等等。
固然不管什麼輪子,要解決的問題都是同樣的:**跨組件狀態共享。**在解決這個核心問題的同時,須要儘量的知足如下幾個特性:前端
最近,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
有些狀態須要依賴其它狀態,這時候就要用 selector
來定義這個狀態了。
好比,咱們須要定義一個新的狀態 filterdInputValue
,它是過濾 inputValue
中的數字後的值。markdown
const filterdInputValue = selector({ key: "filterdInputValue", get: ({get}) => { // 經過 get 能夠讀取其它狀態 const inputValue = get(inputValueState); return inputValue.replace(/[0-9]/ig, ""); }, }); 複製代碼
selector
比較簡單,就是爲了實現狀態的依賴。你能夠在這個 demo 體驗下。
異步
良好的異步請求支持是狀態管理器必不可少的。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,你能夠體驗下。
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>
);
}
複製代碼
當前 Recoil 還處於開發階段,文檔都還不是很全。基於現狀,說幾點個人感覺。
這點我很驚訝,也是寫這個文章的時候才發現的,很奇怪。講道理 Recoil 支持 typescript 應該是順手的事情,可能後期須要來個 @types/recoil
吧。
這個特性應該是必備的,應該不會完全拋棄 Class 組件。估計下個版本確定會支持的這個特性的。實現成本較低,不支持的話就太反人類了。
atom
和
selector
合併成一個等等(也多是我考慮不成熟)。建議官方能夠考慮精簡精簡,原本是一個很簡單的東西,搞的太複雜了。
咱們須要消費一個狀態的時候,須要 import 兩個東西,比較繁瑣。
import { useRecoilState } from "recoil"; import { inputValueState } from "../store"; // 用法 useRecoilState(inputValueState); 複製代碼
原本應該能夠直接經過字符串 key
消費的,但這樣和 redux 問題同樣了,沒法支持 ts。
import { useRecoilState } from "recoil"; useRecoilState('inputValueState'); 複製代碼
不管若是,import 兩個東西不是一個好的用法。
沒有看到讓人眼前一亮的東西,沒有使用衝動。靜觀發展~
Recoil 總體看下來,比較中庸,須要靜觀發展。
另外推薦一下我目前正在用的最簡單的 React 狀態管理器 hox,只有一個 API,很是符合直覺,沒有任何上手成本,徹底擁抱 Hooks 😋。
關注公衆號「前端技術磚家」,拉你進交流羣,你們一塊兒共同交流和進步。