在 React Europe 2020 Conference
上, Facebook
軟件工程師 Dave McCabe
介紹了一個新的狀態管理庫 Recoil
。react
Recoil
如今還處於實驗階段,如今已經在 Facebook
一些內部產品中用於生產環境。畢竟是官方推出的狀態管理框架,以前沒時間仔細研究,藉着國慶期間看了看,給你們分享一下。數據庫
假設咱們有下面一個場景:有 List
和 Canvas
兩個組件,List 中一個節點更新後,Canvas 中的節點也對應更新。redux
最常規則作法是將一個 state
經過父組件分發給 List
和 Canvas
兩個組件,顯然這樣的話每次 state
改變後 全部節點都會全量更新。設計模式
固然,咱們還可使用 Context API
,咱們將節點的狀態存在一個 Context
內,只要 Provider
中的 props
發生改變, Provider
的全部後代使用者都會從新渲染。緩存
爲了不全量渲染的問題,咱們能夠把每一個子節點存儲在單獨的 Context
中,這樣每多一個節點就要增長一層 Provider
。markdown
可是,若是子節點是動態增長的呢?咱們還須要去動態增長 Provider
,這會讓整個樹再次從新渲染,顯然也是不符合預期的。併發
Recoil
自己就是爲了解決 React
全局數據流管理的問題,採用分散管理原子狀態的設計模式。框架
Recoil
提出了一個新的狀態管理單位 Atom
,它是可更新和可訂閱的,當一個 Atom
被更新時,每一個被訂閱的組件都會用新的值來從新渲染。若是從多個組件中使用同一個 Atom
,全部這些組件都會共享它們的狀態。異步
你能夠把 Atom
想象爲爲一組 state
的集合,改變一個 Atom
只會渲染特定的子組件,並不會讓整個父組件從新渲染。async
由於 React
自己提供的 state
狀態在跨組件狀態共享上很是苦難,因此咱們在開發時通常藉助一些其餘的庫如 Redux、Mobx
來幫助咱們管理狀態。這些庫目前正被普遍使用,咱們也並無遇到什麼大問題,那麼 Facebook
爲何還要推出一款新的狀態管理框架呢?
使用 Redux、Mobx
固然能夠,並無什麼問題,主要緣由是它們自己並非 React
庫,咱們是藉助這些庫的能力來實現狀態管理。像 Redux
它自己雖然提供了強大的狀態管理能力,可是使用的成本很是高,你還須要編寫大量冗長的代碼,另外像異步處理或緩存計算也不是這些庫自己的能力,甚至須要藉助其餘的外部庫。
而且,它們並不能訪問 React
內部的調度程序,而 Recoil
在後臺使用 React
自己的狀態,在將來還能提供併發模式這樣的能力。
使用 recoil
狀態的組件須要使用 RecoilRoot
包裹起來:
import React from 'react';
import { RecoilRoot, atom, selector, useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; function App() { return ( <RecoilRoot> <CharacterCounter /> </RecoilRoot> ); } 複製代碼
上面咱們已經提到了 Atom
的概念, Atom
是一種新的狀態,可是和傳統的 state
不一樣,它能夠被任何組件訂閱,當一個 Atom
被更新時,每一個被訂閱的組件都會用新的值來從新渲染。
首先咱們來定義一個 Atom
:
export const nameState = atom({
key: 'nameState', default: 'ConardLi' }); 複製代碼
這種方式意味着你不須要像 Redux
那樣集中定義狀態,能夠像 Mobx
同樣將數據分散定義在任何地方。
要建立一個 Atom
,必需要提供一個 key
,其必須在 RecoilRoot
做用域中是惟一的,而且要提供一個默認值,默認值能夠是一個靜態值、函數甚至能夠是一個異步函數。
Recoil
採用 Hooks
方式訂閱和更新狀態,經常使用的是下面三個 API:
useRecoilState
:相似 useState 的一個 Hook
,能夠取到 atom
的值以及 setter
函useSetRecoilState
:只獲取 setter
函數,若是隻使用了這個函數,狀態變化不會致使組件從新渲染useRecoilValue
:只獲取狀態import { nameState } from './store'
// useRecoilState const NameInput = () => { const [name, setName] = useRecoilState(nameState); const onChange = (event) => { setName(event.target.value); }; return <> <input type="text" value={name} onChange={onChange} /> <div>Name: {name}</div> </>; } // useRecoilValue const SomeOtherComponentWithName = () => { const name = useRecoilValue(nameState); return <div>{name}</div>; } // useSetRecoilState const SomeOtherComponentThatSetsName = () => { const setName = useSetRecoilState(nameState); return <button onClick={() => setName('Jon Doe')}>Set Name</button>; } 複製代碼
selector
表示一段派生狀態,它使咱們可以創建依賴於其餘 atom
的狀態。它有一個強制性的 get
函數,其做用與 redux
的 reselect
或 MobX
的 @computed
相似。
const lengthState = selector({
key: 'lengthState', get: ({get}) => { const text = get(nameState); return text.length; }, }); function NameLength() { const length = useRecoilValue(charLengthState); return <>Name Length: {length}</>; } 複製代碼
selector 是一個純函數:對於給定的一組輸入,它們應始終產生相同的結果(至少在應用程序的生命週期內)。這一點很重要,由於選擇器可能會執行一次或屢次,可能會從新啓動並可能會被緩存。
Recoil
提供了經過數據流圖將狀態和派生狀態映射到 React
組件的方法。真正強大的功能是圖中的函數也能夠是異步的。這使得咱們能夠在異步 React
組件渲染函數中輕鬆使用異步函數。使用 Recoil
,你能夠在選擇器的數據流圖中無縫地混合同步和異步功能。只需從選擇器 get
回調中返回 Promise
,而不是返回值自己。
例以下面的例子,若是用戶名存儲在咱們須要查詢的某個數據庫中,那麼咱們要作的就是返回一個 Promise
或使用一個 async
函數。若是 userID
發生更改,就會自動從新執行新查詢。結果會被緩存,因此查詢將僅對每一個惟一輸入執行一次(因此必定要保證 selector 純函數的特性,不然緩存的結果將會和最新的值不一致)。
const userNameQuery = selector({
key: 'userName', get: async ({get}) => { const response = await myDBQuery({ userID: get(currentUserIDState), }); return response.name; }, }); function CurrentUserInfo() { const userName = useRecoilValue(userNameQuery); return <div>{userName}</div>; } 複製代碼
Recoil
推薦使用 Suspense
,Suspense
將會捕獲全部異步狀態,另外配合 ErrorBoundary
來進行錯誤捕獲:
function MyApp() {
return ( <RecoilRoot> <ErrorBoundary> <React.Suspense fallback={<div>Loading...</div>}> <CurrentUserInfo /> </React.Suspense> </ErrorBoundary> </RecoilRoot> ); } 複製代碼
Recoil
推崇的是分散式的狀態管理,這個模式很相似於 Mobx
,使用起來也感受有點像 observable + computed
的模式,可是其 API 以及核心思想設計的又沒有 Mobx
同樣簡潔易懂,反而有點複雜,對於新手上手起來會有必定成本。
在使用方式上徹底擁抱了函數式的 Hooks
使用方式,並無提供 Componnent
的使用方式,目前使用原生的 Hooks API
咱們也能實現狀態管理,咱們也可使用 useMemo
創造出派生狀態,Recoil
的 useRecoilState
以及 selector
也比較像是對 useContext、useMemo
的封裝。
可是畢竟是 Facebook
官方推出的狀態管理框架,其主打的是高性能以及能夠利用 React
內部的調度機制,包括其承諾即將會支持的併發模式,這一點仍是很是值得期待的。
另外,其自己的分散管理原子狀態的模式、讀寫分離、按需渲染、派生緩存等思想仍是很是值得一學的。
文章中若有錯誤,歡迎在評論區指正;若是文章對你有幫助,歡迎點贊、評論、分享、但願能幫到更多人。
本文首發於公衆號《code祕密花園》歡迎你們關注,原文:Facebook 新一代 React 狀態管理庫 Recoil