React新一代狀態(tài)管理庫(kù)Recoil

由于前段時(shí)間在領(lǐng)導(dǎo)的“威逼利誘”下,了解和學(xué)習(xí)了下Recoil,剛開(kāi)始是比較抗拒的,不過(guò)后來(lái)慢慢的了解了之后,發(fā)現(xiàn)還是很不錯(cuò)的,所以做一個(gè)學(xué)習(xí)的筆記和分享。

Recoil最重要的因?yàn)樗腔贗mmutable的數(shù)據(jù)流管理方案,帶來(lái)的可預(yù)測(cè)性非常利于調(diào)試和維護(hù):

1.斷點(diǎn)調(diào)試時(shí)可預(yù)測(cè),已創(chuàng)建過(guò)的值不會(huì)突變,與斷點(diǎn)位置也無(wú)關(guān)
2.在React框架下組件更新機(jī)制單一只有引用變化才觸發(fā)重新渲染,沒(méi)有forceUpdate的困擾

上手使用

1.初始化:使用Recoil的狀態(tài)的組件需要用RecoilRoot包裹

    import React from 'react';
     import {
        RecoilRoot,
        atom,
        selector,
        useRecoilState,
        useRecoilValue,
        useSetRecoilState
     } from 'recoil';

     function App() {
        return (
            <RecoilRoot>
                <CharacterCounter />
            </RecoilRoot>
        );
     }

2.定義狀態(tài):不需要集中定義,可以像Mobx分散在各個(gè)地方

   export const nameState = atom({
        key: 'nameState',
        default: 'test'
    });

其中key在recoliRoot中是唯一的,并且提供一個(gè)默認(rèn)值,默認(rèn)值可以是靜態(tài)值、函數(shù)、異步函數(shù)等

3.訂閱和更新?tīng)顟B(tài):三個(gè)常用API

1). useRecoilState:類(lèi)似useState的一個(gè)Hook,能夠取到Atom的值以及setter函數(shù)
2). useSetRecoilState:只獲取setter函數(shù),如果只是使用了這個(gè)函數(shù),狀態(tài)更新并不會(huì)引起組件重新渲染
3). useRecoilValue:只獲取狀態(tài)

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>;
}

4.派生狀態(tài):與Mobx的computed類(lèi)似,selector表示一段派生狀態(tài),提供了get、set、分別定義如何賦值,如何取值,同時(shí)其與atom定義的一樣可以使用上述三種API。

const lengthState = selector({
  key: 'lengthState', 
  get: ({get}) => {
    const text = get(nameState);
    return text.length;
  },
});

function NameLength() {
  const length = useRecoilValue(lengthState);
  return <>Name Length: {length}</>;
}

5.異步狀態(tài):基于selector可以實(shí)現(xiàn)異步數(shù)據(jù)讀取,即修改get函數(shù)為異步函數(shù)

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>;
}

function MyApp() {
  return (
    <RecoilRoot>
      <ErrorBoundary>
        <React.Suspense fallback={<div>Loading...</div>}>
          <CurrentUserInfo />
        </React.Suspense>
      </ErrorBoundary>
    </RecoilRoot>
  );
}

其中,異步狀態(tài)可以被Suspense捕獲,異步過(guò)程報(bào)錯(cuò)可以被ErrorBoundary捕獲。
如果不想用Suspense異步阻塞,可以使用useRecoilValueLoadable在當(dāng)前組件內(nèi)管理異步狀態(tài)

不足

  • Immutable壓力:API繁多,而且Immutable模式中,對(duì)于數(shù)據(jù)流只有讀和寫(xiě)兩種訴求,但是我們期待讀的含義是,UI能夠在訂閱其變化后自然而然Rerender。
    Recoil提供了useRecoilState作為讀寫(xiě)雙重API,useRecoilValue只是簡(jiǎn)化了API,但是useSetRecoilValue在僅寫(xiě)不讀的場(chǎng)景下,是不會(huì)隨著狀態(tài)變更重新渲染組件的。
    對(duì)比useState,他是單組件狀態(tài)管理的場(chǎng)景,但Recoil是全局狀態(tài)解決方案,讀寫(xiě)分離的場(chǎng)景下,對(duì)于只寫(xiě)的組件很有必要脫離對(duì)數(shù)據(jù)的訂閱實(shí)現(xiàn)性能最大化。
  • 條件訪問(wèn)數(shù)據(jù):因?yàn)镠ooks的通病,無(wú)法寫(xiě)在條件語(yǔ)句中,所以要利用 Hooks 獲取一個(gè)帶有條件判斷的數(shù)據(jù)時(shí),必須回到 selector 模式。
    從useRecoilState以及selector來(lái)看,相當(dāng)于Recoil對(duì)useContext和useMemo的封裝。

收獲

盡管短時(shí)間內(nèi)我們不會(huì)在項(xiàng)目上Recoil,但是它帶給我們的絕不只是上述的用法,在狀態(tài)管理上,我們或許可以思考新的出發(fā)點(diǎn):

  • 讀與寫(xiě)分離,做到最優(yōu)按需渲染
  • 派生的值必須嚴(yán)格緩存,并在命中緩存時(shí)引用保證嚴(yán)格相等
  • 原子存儲(chǔ)的數(shù)據(jù)相互無(wú)關(guān)聯(lián),所有關(guān)聯(lián)的數(shù)據(jù)都使用派生值方式推導(dǎo)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容