1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Recoil:Facebook 新一代的 React 狀態(tài)管理庫

        共 9716字,需瀏覽 20分鐘

         ·

        2021-04-19 14:56

        大廠技術(shù)  堅持周更  精選好文

        本文主要介紹facebook出的狀態(tài)管理庫Recoil(非react官方)。

        其優(yōu)點

        1. 避免類似Redux和Mobx這樣的庫帶來的開銷。
        2. 規(guī)避Context 的局限性。

        其缺點:

        1. 目前只支持hooks 。
        2. 處于實驗階段,穩(wěn)定性有待觀察。

        引言

        Redux

        放一張很熟悉的圖。redux的狀態(tài)管理如下圖所示。

        Mobx

        • Observable State, 所有可以改變的值。

        • Derivation:

          • Computed Value(又稱Derivation), 是可以用純函數(shù)從當(dāng)前可觀察狀態(tài)中衍生出的值。
        • Reaction, 與Computed Value類似也是基于Observable State 。當(dāng)狀態(tài)改變時需要自動發(fā)生的副作用,用來連接命令式編程和響應(yīng)式編程,最終都需要實現(xiàn)I/O操作,例如發(fā)送請求,更新頁面等。

        • Action, 所有修改Observable State的動作,用戶事件,后端數(shù)據(jù)推送等。

        • 注:可變數(shù)據(jù)流。(如果需要Mutable方式管理react狀態(tài),可以參考Mobx中文文檔[1])。

        兩者聯(lián)系與區(qū)別:

        • 編程方式:redux 更加偏向函數(shù)式編程,Mobx思想上更加偏向面向?qū)ο缶幊毯晚憫?yīng)式編程。
        • 數(shù)據(jù)存儲方式不同:Redux將數(shù)據(jù)保存在單一store中,Mobx將數(shù)據(jù)保存在分散的多個store中。
        • 狀態(tài)存儲的形式:
          • redux存儲的js原生對象形式:需要手動追蹤狀態(tài)的變化。
        • Mobx會將該狀態(tài)包裝成一個可觀察對象,并自動追蹤這個狀態(tài)的更新。
        • 數(shù)據(jù)是否是可變狀態(tài):Redux更多的偏向使用不可變狀態(tài),不能直接去修改它,而是應(yīng)該使用純函數(shù)返回一個新的狀態(tài)。Mobx中的狀態(tài)是可以直接修改的。https://juejin.cn/post/6844903797085437966[2]

        State 與 Content

        問題: State 與 Content 存在的問題

        場景:有 List 和 Canvas 兩個組件,List 中節(jié)點更新,Canvas 中對應(yīng)的節(jié)點也更新。

        第一種方法:將 State 傳到公共父節(jié)點。

        缺點: 會全量re-render。

        第二種方法:給父節(jié)點加 Provider 在子節(jié)點加 Consumer,不過每多加一個 item 就要多一層 Provider。

        一. 介紹:

        在構(gòu)建一個react應(yīng)用時一個令人頭痛的問題是狀態(tài)管理。雖然目前有較為成熟的狀態(tài)管理庫如redux和Mobx,使用他們所帶來的開銷也是難以估量的。當(dāng)然最理想的方法是使用react來進行狀態(tài)管理。

        但是這帶來了以下三個問題。組件狀態(tài)只能與其祖先組件進行共享,這可能會帶來組件樹中大量的重繪開銷。Context 只能保存一個特定值而不是與其 Consumer 共享一組不確定的值。

        以上兩點導(dǎo)致組件樹頂部組件(狀態(tài)生產(chǎn)者)與組件樹底部組件(狀態(tài)消費者)之間的代碼拆分變得非常困難 Recoil 在組件樹中定義了一個正交且內(nèi)聚的單向圖譜。狀態(tài)變更通過以下方法從圖譜的底部(atoms)通過純函數(shù)(selectors)進入組件。

        思想:將組件中的狀態(tài)單獨抽離出來,構(gòu)成一個獨立于組件的狀態(tài)樹,樹的底部是atom通過selectors進入組件。

        如圖所示。提供了一些無依賴的方法,這些方法像 React 局部狀態(tài)一樣暴露相同的 get/set 接口(簡單理解為 reducers 之類的概念亦可)。

        我們能夠與一些 React 新功能(比如并發(fā)模式)兼容。狀態(tài)定義是可伸縮和分布式的,代碼拆分成為可能。

        不用修改組件即可派生數(shù)據(jù)狀態(tài)。派生數(shù)據(jù)狀態(tài)支持同步和異步。把跳轉(zhuǎn)看作一級概念,甚至可以對鏈接中的狀態(tài)流轉(zhuǎn)進行編碼。

        所以可以簡單地使用向后兼容的方式來持久化整個應(yīng)用的狀態(tài),應(yīng)用變更時持久化狀態(tài)也可以因此得以保留。可以把 Atom 想象為為一組 state 的集合,改變一個 Atom 只會渲染特定的子組件,并不會讓整個父組件重新渲染。與Redux和Mobx相比,redux與Mobx 不能訪問React內(nèi)部調(diào)度的程序。而recoil在后臺使用React本身的狀態(tài)。

        二. 主要概念

        Atoms - 共享狀態(tài)

        組件可訂閱的最小狀態(tài)單元-可被定義和更新類似于setState中的state。(一般定義一些基礎(chǔ))

        const todoListState = atom({
          key'todoListState'//key是RecoilRoot 作用域內(nèi)唯一的
        default: [],
        });

        Selector(derived state) - 純函數(shù)

        一個selector代表一個派生的狀態(tài)(由基礎(chǔ)的狀態(tài)atom派生)。入?yún)⑹茿toms/Selector類型的純函數(shù)。當(dāng)它的上游改變時,它會自動更新。其使用方法和Atom基本類似。

        const fontSizeLabelState = selector({
          key'fontSizeLabelState',
          get({get}) => {
        const fontSize = get(fontSizeState);
        const unit = 'px';
        return `${fontSize}${unit}`;
          },
          set: ({get, set},newValue) => {
              return set('',newValue)
          },
        });
        • Key:  與atom 的key一樣的作用具有唯一性。
        • Get屬性:定義如何取值。是一個計算函數(shù),可以使用get字段來訪問輸入的Atom和Selector。當(dāng)其所依賴的狀態(tài)更新時,改狀態(tài)也會跟著更新。
        • Set :返回新的可寫狀態(tài)的可選函數(shù)。

        注:只有同時具有g(shù)et和set的selector才具備可讀寫屬性。set: 設(shè)置原子值的函數(shù)。

        相關(guān)hooks

        • useRecoilValue():對Atom/Selector進行讀操作(有些Selector只有可讀屬性沒有可寫屬性)。
        function TodoList({
        const todoList = useRecoilValue(todoListState);
        return (
            <>
              <TodoItemCreator />
              {todoList.map((todoItem) => (
                <TodoItem key={todoItem.id} item={todoItem} />
              ))}
            </>

          );
        }
        • useSetRecoilState():對Atom/Selector進行寫操作。

        其他相關(guān)hooks

        function TodoItemCreator({
        const [inputValue, setInputValue] = useState('');
        const setTodoList = useSetRecoilState(todoListState);
        const addItem = () => {
            setTodoList((oldTodoList) => [
              ...oldTodoList,
              {
                id: getId(),
                text: inputValue,
                isCompletefalse,
              },
            ]);
            setInputValue('');
          };
        const onChange = ({target: {value}}) => {
            setInputValue(value);
          };
        return (
            <div>
              <input type="text" value={inputValue} onChange={onChange} />
              <button onClick={addItem}>Add</button>
            </div>

          );
        }
        // utility for creating unique Id
        let id = 0;
        function getId({
        return id++;
        }
        • useRecoilState(): 對原子進行讀寫操作。
        • useResetRecoilState():重置原子的默認值。

        useSetRecoilState 與 useRecoilState 的不同之處在于,數(shù)據(jù)流的變化不會導(dǎo)致組件 Rerende, useSetRecoilState僅僅是寫入該原子, 沒有訂閱該原子以及原子的更新。

        注:所有的Atom都是可讀寫的狀態(tài)。

        <RecoilRoot ...props>

        全局的數(shù)據(jù)流管理需要在RecoilRoot作用域上才可以,被嵌套時最內(nèi)層會嵌套外曾的作用域。

        三. 異步處理:

        • Sync

        同步狀態(tài)下,只要上游的數(shù)據(jù)變了它就會自動改變。如上文所示。

        • Async

        只需要get函數(shù)返回的是一個promise即可。Recoil 對于異步處理是需要與React Suspense[3] 一起來處理異步的數(shù)據(jù)。如果任何依賴項發(fā)生更改,將重新計算選擇器并執(zhí)行新查詢。會對結(jié)果進行緩存,如果輸入一樣將不會進行查詢,對相同的輸入也只會進行一次查詢。

        • 例子:
        const currentUserNameQuery = selector({
          key'CurrentUserName',
          getasync ({get}) => {
        const response = await myDBQuery({
              userIDget(currentUserIDState),
            });
        return response.name;
          }
        });
        function CurrentUserInfo() {
        const userName = useRecoilValue(currentUserNameQuery);
        return <div>{userName}</div>;
        }
        //處于pending狀態(tài)會將promise拋出,交給suspense來處理。
        function MyApp({
        return (
            <RecoilRoot>
              <React.Suspense fallback={<div>Loading...</div>}>
                <CurrentUserInfo />
              </React.Suspense>
            </RecoilRoot>

          );
        }
        • 異步狀態(tài)可以被 Suspence 捕獲。
        • 異步過程報錯可以被ErrorBoundary 捕獲。

        不使用Suspence

        除了使用Suspence來處理異步的selector,還可以使用useRecoilValueLoadable()這個Api在當(dāng)前組件。

        function UserInfo({userID}{
        const userNameLoadable = useRecoilValueLoadable(userNameQuery(userID));
        switch (userNameLoadable.state) {
        case 'hasValue':
        return <div>{userNameLoadable.contents}</div>;
        case 'loading':
        return <div>Loading...</div>;
        case 'hasError':
        throw userNameLoadable.contents;
          }
        }

        可以通過state的狀態(tài)來讀取到異步的請求。

        依賴外部變量進行查詢

        有些時候需要使用其他參數(shù)(而不是Atom/Select)來進行數(shù)據(jù)查詢。

        const userNameQuery = selectorFamily({
          key'UserName',
          get(userID) => async ({get}) => {
        const response = await myDBQuery({userID});
        if (response.error) {
        throw response.error;
            }
        return response.name;
          },
        });
        function UserInfo({userID}{
        const userName = useRecoilValue(userNameQuery(userID));
        return <div>{userName}</div>;
        }

        四. Utils

        • atomFamily()
          • 與autom()類似,不同的是atomFamily返回一個函數(shù),該函數(shù)接受一個參數(shù)。可以根據(jù)這個參數(shù)來提供不同的Atom.
        const elementPositionStateFamily = atomFamily({
          key: 'ElementPosition',
        default: [0, 0],
        });
        function ElementListItem({elementID}) {
        const position = useRecoilValue(elementPositionStateFamily(elementID));
        return (
            <div>
              Element: {elementID}
              Position: {position}
            </div>
          );
        }
        • 默認值可以根據(jù)傳入的參數(shù)進行改變。
        const myAtomFamily = atomFamily({
          key: ‘MyAtom’,
          default: param => defaultBasedOnParam(param),
        });
        • selectorFamily()

        • 與Selector類似,但是可以將參數(shù)傳遞給set和get屬性。

        const myNumberState = atom({
          key: 'MyNumber',
        default: 2,
        });
        const myMultipliedState = selectorFamily({
          key: 'MyMultipliedNumber',
          get: (multiplier) => ({get}) => {
        return get(myNumberState) * multiplier;
          },
        // optional set
          set: (multiplier) => ({set}, newValue) => {
        set(myNumberState, newValue / multiplier);
          },
        });
        function MyComponent() {
        // defaults to 2
        const number = useRecoilValue(myNumberState);
        // defaults to 200
        const multipliedNumber = useRecoilValue(myMultipliedState(100));
        return <div>...</div>;
        }
        • 那么就可以通過這樣將其依賴的值傳遞進去,從而進行數(shù)據(jù)查詢。

        五. 與Hox狀態(tài)管理庫相比

        1. 與hox相比:
          1. Recoi由facebook1. 來自facebook官方實驗項目, 仍然處于可觀察。2. Api較多。
          2. hox由1. 螞蟻金服來維護的,處于相對穩(wěn)定的狀態(tài)。2. Api較少。

        總結(jié):

        Recoil 將應(yīng)用中的狀態(tài)抽離出來組成一個狀態(tài)樹,通過selector來與組件進行溝通。其與App中的組件呈正交性。優(yōu)點:Recoil 在后臺使用的是React本身的狀態(tài)。使用方式上完全支持hooks。未來會是一個值得期待的狀態(tài)管理框架。

        參考文獻:

        1. Recoil 文檔[4]
        2. Recoil [5]
        3. You Might Not Need Redux[6]
        4. YouTube-Recoil[7]
        5. Mobx中文文檔[8]
        6. 帶你走進Mobx的原理[9]
        7. 你需要Mobx還是Redux?[10]

        參考資料

        [1]

        Mobx中文文檔: https://cn.mobx.js.org/

        [2]

        https://juejin.cn/post/6844903797085437966: https://juejin.cn/post/6844903797085437966

        [3]

        React Suspense: https://reactjs.org/docs/concurrent-mode-suspense.html

        [4]

        Recoil 文檔: https://recoil.js.cn/docs/guides/asynchronous-data-queries

        [5]

        Recoil : https://bytedance.feishu.cn/wiki/wikcnrGEa9YON5PqlxC7sMJSymc

        [6]

        You Might Not Need Redux: https://medium.com/@dan_abramov/you-might-not-need-redux-be46360cf367

        [7]

        YouTube-Recoil: https://www.youtube.com/watch?v=_ISAA_Jt9kI

        [8]

        Mobx中文文檔: https://cn.mobx.js.org/

        [9]

        帶你走進Mobx的原理: https://juejin.cn/post/6844903797085437966#heading-6

        [10]

        你需要Mobx還是Redux?: https://juejin.cn/post/6844903562095362056

        瀏覽 65
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        評論
        圖片
        表情
        推薦
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            久久国产免费电影 | 日小逼网站我 | 国产91精品入口福利 | 公车上拨开她湿润的内裤的视频 | 青青草A片成人网站免费看 | 国产在线观看国产精品产拍 | 大桥未久一区二区 | 男女叉叉叉 | 青娱乐网站 | 99热这里只有精品在线 |