<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

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

          共 9738字,需瀏覽 20分鐘

           ·

          2021-04-16 21:56




          本文主要介紹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

          ?? 謝謝支持

          1. 喜歡的話別忘了 分享、點贊、在看 三連哦~。

          2. 點擊下方名片,關(guān)注 前端Sharing


          瀏覽 43
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

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

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  cao在线视频 | 操学生妹在线播放 | 久久女人高朝视频免费看 | 天美传媒69成人影片 | 黄色毛片学生妹免费看视频 |