国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

[源碼閱讀]純粹極簡的react狀態管理組件unstated

FrancisSoung / 1141人閱讀

摘要:此處繼承了上面的可以注入現成的狀態管理實例,添加到之中。返回值寫成的意義簡單一句話概括,這么寫可以避免改變導致子組件的重復渲染。就是創建狀態管理組件時默認傳遞的監聽函數,用的是的更新一個空對象。返回值寫成的意義。

簡介

unstated是一個極簡的狀態管理組件

看它的簡介:State so simple, it goes without saying
對比 對比redux:

更加靈活(相對的缺點是缺少規則,需要使用者的自覺)

redux的狀態是存放在一棵樹內,采用嚴格的單向流

unstated的狀態是用戶自己定義,說白了就是object,可以放在一個組件的內,也可以放在多個組件內

針對React,一致的API

redux必須編寫reduceraction,通過dispatch(action)改變狀態,它不限框架

unstated改變狀態的API完全與React一致,使用this.setState,當然和ReactsetState不同,
但是它的底層也是用到了setState去更新視圖

功能相對簡單

unstated沒有中間件功能,每次狀態改變(不管是否相等),都會重新渲染(V2.1.1)

可以自定義listener,每次更新狀態時都會執行。

對比React的自帶state:

天生將組件分割為Container(狀態管理)Component(視圖管理)

靈活配置共享狀態或者私有狀態

支持promise

快速了解請直接跳到總結
初識

3大板塊和幾個關鍵變量

Provider: 注入狀態實例,傳遞map,本質是Context.Provider,可嵌套達成鏈式傳遞
Container: 狀態管理類,遵循React的API,發布訂閱模式,通過new生成狀態管理實例
Subscribe: 訂閱狀態組件,本質是Context.Consumer,接收Provider提供的map,視圖渲染組件
map: new Map(),通過類查找當前類創建的狀態管理實例
深入

這里引入官方例子

// @flow
import React from "react";
import { render } from "react-dom";
import { Provider, Subscribe, Container } from "unstated";

type CounterState = {
  count: number
};
// 定義一個狀態管理類
class CounterContainer extends Container {
  state = {
    count: 0
  };

  increment() {
    this.setState({ count: this.state.count + 1 });
  }

  decrement() {
    this.setState({ count: this.state.count - 1 });
  }
}
// 渲染視圖組件(Context.Consumer的模式)
function Counter() {
  return (
    
      {counter => (
        
{counter.state.count}
)}
); } render( , document.getElementById("root") );

這里Counter是我們自定義的視圖組件,首先使用包裹,接著在Counter內部,調用組件,
傳遞一個數組給props.to,這個數組內存放了Counter組件需要使用的狀態管理類(此處也可傳遞狀態管理實例)。

Provider
export function Provider(props: ProviderProps) {
  return (
    
      {parentMap => {
        let childMap = new Map(parentMap);
        // 外部注入的狀態管理實例
        if (props.inject) {
          props.inject.forEach(instance => {
            childMap.set(instance.constructor, instance);
          });
        }

        // 負責將childMap傳遞,初始為null
        return (
          
            {props.children}
          
        );
      }}
    
  );
}

這里的模式是


  ()=>{
    /* ... */
    return {props.children}
  }
  

有3個注意點:

外層嵌套可以嵌套調用。


 /* ... */
 
 /* ... */ 

props.inject可以注入現成的狀態管理實例,添加到map之中。

返回值寫成props.children

返回值寫成props.children的意義

簡單一句話概括,這么寫可以避免React.Context改變導致子組件的重復渲染。

具體看這里:避免React Context導致的重復渲染

Container
export class Container {
  // 保存狀態 默認為{}
  state: State;
  // 保存監聽函數,默認為[]
  _listeners: Array = [];

  setState(
    updater: $Shape | ((prevState: $Shape) => $Shape),
    callback?: () => void
  ): Promise {
    return Promise.resolve().then(() => {
      let nextState;

      /* 利用Object.assign改變state */

      // 執行listener(promise)
      let promises = this._listeners.map(listener => listener());

      // 所有Promise執行完畢
      return Promise.all(promises).then(() => {
        // 全部listener執行完畢,執行回調
        if (callback) {
          return callback();
        }
      });
    });
  }

  // 增加訂閱(這里默認的訂閱就是React的setState空值(為了重新渲染),也可以添加自定義監聽函數)
  subscribe(fn: Listener) {
    this._listeners.push(fn);
  }

  // 取消訂閱
  unsubscribe(fn: Listener) {
    this._listeners = this._listeners.filter(f => f !== fn);
  }
}

Container內部邏輯很簡單,改變state,執行監聽函數。

其中有一個_listeners,是用于存放監聽函數的。

每個狀態管理實例存在一個默認監聽函數onUpdate
這個默認的監聽函數的作用就是調用React的setState強制視圖重新渲染

這里的監聽函數內部返回Promise,最后通過Promise.all確保執行完畢,然后執行回調參數

因此setState在外面使用也可以使用then

例如,在官方例子中:

increment() {
    this.setState({ count: this.state.count + 1 },()=>console.log("2"))
    .then(()=>console.log("3") )
    console.log("1") 
  }
  // 執行順序是 1 -> 2 ->3

2個注意點:

setStateReact API一致,第一個參數傳入object或者function,第二個傳入回調

這里通過Promise.resolve().then模擬this.setState的異步執行

關于Promise.resolve和setTimeout的區別

簡單的說兩者都是異步調用,Promise更快執行。

setTimeout(()=>{},0)會放入下一個新的任務隊列

Promise.resolve().then({})會放入微任務,在調用棧為空時立刻補充調用棧并執行(簡單理解為當前任務隊列尾部)

更多詳細可以看這里提供的2個視頻:https://stackoverflow.com/a/38752743

Subscribe
export class Subscribe extends React.Component<
  SubscribeProps,
  SubscribeState
> {
  state = {};
  // 存放傳入的狀態組件
  instances: Array = [];
  unmounted = false;

  componentWillUnmount() {
    this.unmounted = true;
    this._unsubscribe();
  }

  _unsubscribe() {
    this.instances.forEach(container => {
      // container為當前組件的每一個狀態管理實例
      // 刪除listeners中的this.onUpdate
      container.unsubscribe(this.onUpdate);
    });
  }

  onUpdate: Listener = () => {
    return new Promise(resolve => {
      // 組件未被卸載
      if (!this.unmounted) {
        // 純粹是為了讓React更新組件
        this.setState(DUMMY_STATE, resolve);
      } else {
        // 已經被卸載則直接返回
        resolve();
      }
    });
  };
  
  /* ... */
}

這里的關鍵就是instances,用于存放當前組件的狀態管理實例

當組件unmount的時候,會unsubscribe當前狀態管理實例的默認監聽函數,那么如果當前的狀態管理實例是共享的,會不會有影響呢?

不會的。往后看可以知道,當state每次更新,都會重新創建新的狀態管理實例(因為props.to的值可能會發生變化,例如取消某一個狀態管理實例),
而每次創建時,都會先unsubscribesubscribe,確保不會重復添加監聽函數。

onUpdate就是創建狀態管理組件時默認傳遞的監聽函數,用的是ReactsetState更新一個DUMMY_STATE(空對象{})。

export class Subscribe extends React.Component<
  SubscribeProps,
  SubscribeState
> {
  /* 上面已講 */

  _createInstances(
    map: ContainerMapType | null,
    containers: ContainersType
  ): Array {
    // 首先全部instances解除訂閱
    this._unsubscribe();

    // 必須存在map 必須被Provider包裹才會有map
    if (map === null) {
      throw new Error(
        "You must wrap your  components with a "
      );
    }

    let safeMap = map;
    // 重新定義當前組件的狀態管理組件(根據to傳入的數組)
    let instances = containers.map(ContainerItem => {
      let instance;

      // 傳入的是Container組件,則使用
      if (
        typeof ContainerItem === "object" &&
        ContainerItem instanceof Container
      ) {
        instance = ContainerItem;
      } else {
        // 傳入的不是Container,可能是其他自定義組件等等(需要用new執行),嘗試獲取
        instance = safeMap.get(ContainerItem);

        // 不存在則以它為key,value是新的Container組件
        if (!instance) {
          instance = new ContainerItem();
          safeMap.set(ContainerItem, instance);
        }
      }

      // 先解綁再綁定,避免重復訂閱
      instance.unsubscribe(this.onUpdate);
      instance.subscribe(this.onUpdate);

      return instance;
    });

    this.instances = instances;
    return instances;
  }
  
  /* ... */
}

_createInstances內部,如果檢查到傳入的props.to的值已經是狀態管理實例(私有狀態組件),那么直接使用即可,
如果傳入的是類class(共享狀態組件),會嘗試通過查詢map,不存在的則通過new創建。

export class Subscribe extends React.Component<
  SubscribeProps,
  SubscribeState
> {
  
  /* 上面已講 */
  
  render() {
    return (
      
      /* Provider傳遞的map */
      {map =>
          // children是函數
          this.props.children.apply(
            null,
            // 傳給子函數的參數(傳進當前組件的狀態管理實例)
            this._createInstances(map, this.props.to)
          )
        }
      
    );
  }
}

每一次render都會創建新的狀態管理實例

到此,3大板塊已經閱讀完畢。

總結

簡單易用,與React一致的API,一致的書寫模式,讓使用者很快上手。

并沒有規定如何管理這些狀態管理類,非常靈活。

我們可以學redux將所有狀態放到一個共享狀態管理實例內部,
例如通過Providerinject屬性注入,

或者針對每一個組件創建多帶帶的狀態管理實例(可共享可獨立)(unstated作者推薦),

一切可以按照自己的想法,但同時也要求使用者自己定義一些規則去約束寫法。

僅僅是管理了狀態,每次更新都是一個全新的instance集合,并沒有做任何對比,需要我們在視圖層自己實現。

返回值寫成props.children的意義。

關于Promise.resolve().then({})setTimeout(()=>{},0)的區別。

導圖

源碼閱讀專欄對一些中小型熱門項目進行源碼閱讀和分析,對其整體做出導圖,以便快速了解內部關系及執行順序。
當前源碼(帶注釋),以及更多源碼閱讀內容:https://github.com/stonehank/sourcecode-analysis,歡迎fork,求

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/99808.html

相關文章

  • 理解 React 輕量狀態管理Unstated

    摘要:返回,用來包裹頂層組件,向應用中注入狀態管理實例,可做數據的初始化。方法返回創建的狀態管理實例,作為參數傳遞給調用的函數,函數拿到實例,操作或顯示數據。用來實現一個狀態管理類。為中的狀態管理實例數據。 個人網站: https://www.neroht.com 在React寫應用的時候,難免遇到跨組件通信的問題。現在已經有很多的解決方案。 React本身的Context Redux結合...

    Profeel 評論0 收藏0
  • React組件設計實踐總結05 - 狀態管理

    摘要:要求通過要求數據變更函數使用裝飾或放在函數中,目的就是讓狀態的變更根據可預測性單向數據流。同一份數據需要響應到多個視圖,且被多個視圖進行變更需要維護全局狀態,并在他們變動時響應到視圖數據流變得復雜,組件本身已經無法駕馭。今天是 520,這是本系列最后一篇文章,主要涵蓋 React 狀態管理的相關方案。 前幾篇文章在掘金首發基本石沉大海, 沒什么閱讀量. 可能是文章篇幅太長了?掘金值太低了? ...

    ideaa 評論0 收藏0
  • 讀zent源碼庫之Dialog組件實現

    摘要:但是,最后一步,事件怎么綁定呢這塊沒有深入研究了,不過我想,應該這樣去實現也是沒有問題的。的具體做法是,把方法放到了一個叫做的組件上去實現這個功能,然后再把內容放進這個組件。其他的邏輯比如顯示隱藏之類,全部都放到組件自身上去實現。 1、Dialog組件提供什么功能,解決什么問題? zent的Dialog組件,使用姿勢是這樣的(代碼摘自zent官方文檔:https://www.youza...

    陳江龍 評論0 收藏0
  • 前端每周清單第 50 期: AngularJS and Long Term Support, Web

    摘要:在該版本發布之后,開發團隊并不會繼續發布新的特性,而會著眼于進行重大的錯誤修復。發布每六個星期,團隊就會創建新的分支作為發布通道,本文即是對新近發布的版本進行簡要介紹。 showImg(https://segmentfault.com/img/remote/1460000013229009); 前端每周清單專注前端領域內容,以對外文資料的搜集為主,幫助開發者了解一周前端熱點;分為新聞熱...

    DobbyKim 評論0 收藏0
  • React 新 Context API 在前端狀態管理的實踐

    摘要:本文轉載至今日頭條技術博客眾所周知,的單向數據流模式導致狀態只能一級一級的由父組件傳遞到子組件,在大中型應用中較為繁瑣不好管理,通常我們需要使用來幫助我們進行管理,然而隨著的發布,新成為了新的選擇。 本文轉載至:今日頭條技術博客showImg(https://segmentfault.com/img/bVbiNJO?w=900&h=383);眾所周知,React的單向數據流模式導致狀態...

    wing324 評論0 收藏0

發表評論

0條評論

FrancisSoung

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<