成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

[源碼閱讀]純粹極簡的react狀態(tài)管理組件unstated

FrancisSoung / 1147人閱讀

摘要:此處繼承了上面的可以注入現(xiàn)成的狀態(tài)管理實(shí)例,添加到之中。返回值寫成的意義簡單一句話概括,這么寫可以避免改變導(dǎo)致子組件的重復(fù)渲染。就是創(chuàng)建狀態(tài)管理組件時(shí)默認(rèn)傳遞的監(jiān)聽函數(shù),用的是的更新一個(gè)空對象。返回值寫成的意義。

簡介

unstated是一個(gè)極簡的狀態(tài)管理組件

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

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

redux的狀態(tài)是存放在一棵樹內(nèi),采用嚴(yán)格的單向流

unstated的狀態(tài)是用戶自己定義,說白了就是object,可以放在一個(gè)組件的內(nèi),也可以放在多個(gè)組件內(nèi)

針對React,一致的API

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

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

功能相對簡單

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

可以自定義listener,每次更新狀態(tài)時(shí)都會執(zhí)行。

對比React的自帶state:

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

靈活配置共享狀態(tài)或者私有狀態(tài)

支持promise

快速了解請直接跳到總結(jié)
初識

3大板塊和幾個(gè)關(guān)鍵變量

Provider: 注入狀態(tài)實(shí)例,傳遞map,本質(zhì)是Context.Provider,可嵌套達(dá)成鏈?zhǔn)絺鬟f
Container: 狀態(tài)管理類,遵循React的API,發(fā)布訂閱模式,通過new生成狀態(tài)管理實(shí)例
Subscribe: 訂閱狀態(tài)組件,本質(zhì)是Context.Consumer,接收Provider提供的map,視圖渲染組件
map: new Map(),通過類查找當(dāng)前類創(chuàng)建的狀態(tài)管理實(shí)例
深入

這里引入官方例子

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

type CounterState = {
  count: number
};
// 定義一個(gè)狀態(tài)管理類
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內(nèi)部,調(diào)用組件,
傳遞一個(gè)數(shù)組給props.to,這個(gè)數(shù)組內(nèi)存放了Counter組件需要使用的狀態(tài)管理類(此處也可傳遞狀態(tài)管理實(shí)例)。

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

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

這里的模式是


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

有3個(gè)注意點(diǎn):

外層嵌套可以嵌套調(diào)用。


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

props.inject可以注入現(xiàn)成的狀態(tài)管理實(shí)例,添加到map之中。

返回值寫成props.children。

返回值寫成props.children的意義

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

具體看這里:避免React Context導(dǎo)致的重復(fù)渲染

Container
export class Container {
  // 保存狀態(tài) 默認(rèn)為{}
  state: State;
  // 保存監(jiān)聽函數(shù),默認(rèn)為[]
  _listeners: Array = [];

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

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

      // 執(zhí)行l(wèi)istener(promise)
      let promises = this._listeners.map(listener => listener());

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

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

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

Container內(nèi)部邏輯很簡單,改變state,執(zhí)行監(jiān)聽函數(shù)。

其中有一個(gè)_listeners,是用于存放監(jiān)聽函數(shù)的。

每個(gè)狀態(tài)管理實(shí)例存在一個(gè)默認(rèn)監(jiān)聽函數(shù)onUpdate,
這個(gè)默認(rèn)的監(jiān)聽函數(shù)的作用就是調(diào)用React的setState強(qiáng)制視圖重新渲染

這里的監(jiān)聽函數(shù)內(nèi)部返回Promise,最后通過Promise.all確保執(zhí)行完畢,然后執(zhí)行回調(diào)參數(shù)

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

例如,在官方例子中:

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

2個(gè)注意點(diǎn):

setStateReact API一致,第一個(gè)參數(shù)傳入object或者function,第二個(gè)傳入回調(diào)

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

關(guān)于Promise.resolve和setTimeout的區(qū)別

簡單的說兩者都是異步調(diào)用,Promise更快執(zhí)行。

setTimeout(()=>{},0)會放入下一個(gè)新的任務(wù)隊(duì)列

Promise.resolve().then({})會放入微任務(wù),在調(diào)用棧為空時(shí)立刻補(bǔ)充調(diào)用棧并執(zhí)行(簡單理解為當(dāng)前任務(wù)隊(duì)列尾部)

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

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

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

  _unsubscribe() {
    this.instances.forEach(container => {
      // container為當(dāng)前組件的每一個(gè)狀態(tài)管理實(shí)例
      // 刪除listeners中的this.onUpdate
      container.unsubscribe(this.onUpdate);
    });
  }

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

這里的關(guān)鍵就是instances,用于存放當(dāng)前組件的狀態(tài)管理實(shí)例

當(dāng)組件unmount的時(shí)候,會unsubscribe當(dāng)前狀態(tài)管理實(shí)例的默認(rèn)監(jiān)聽函數(shù),那么如果當(dāng)前的狀態(tài)管理實(shí)例是共享的,會不會有影響呢?

不會的。往后看可以知道,當(dāng)state每次更新,都會重新創(chuàng)建新的狀態(tài)管理實(shí)例(因?yàn)?b>props.to的值可能會發(fā)生變化,例如取消某一個(gè)狀態(tài)管理實(shí)例),
而每次創(chuàng)建時(shí),都會先unsubscribesubscribe,確保不會重復(fù)添加監(jiān)聽函數(shù)。

onUpdate就是創(chuàng)建狀態(tài)管理組件時(shí)默認(rèn)傳遞的監(jiān)聽函數(shù),用的是ReactsetState更新一個(gè)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;
    // 重新定義當(dāng)前組件的狀態(tài)管理組件(根據(jù)to傳入的數(shù)組)
    let instances = containers.map(ContainerItem => {
      let instance;

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

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

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

      return instance;
    });

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

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

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

每一次render都會創(chuàng)建新的狀態(tài)管理實(shí)例。

到此,3大板塊已經(jīng)閱讀完畢。

總結(jié)

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

并沒有規(guī)定如何管理這些狀態(tài)管理類,非常靈活。

我們可以學(xué)redux將所有狀態(tài)放到一個(gè)共享狀態(tài)管理實(shí)例內(nèi)部,
例如通過Providerinject屬性注入,

或者針對每一個(gè)組件創(chuàng)建多帶帶的狀態(tài)管理實(shí)例(可共享可獨(dú)立)(unstated作者推薦),

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

僅僅是管理了狀態(tài),每次更新都是一個(gè)全新的instance集合,并沒有做任何對比,需要我們在視圖層自己實(shí)現(xiàn)。

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

關(guān)于Promise.resolve().then({})setTimeout(()=>{},0)的區(qū)別。

導(dǎo)圖

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

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99808.html

相關(guān)文章

  • 理解 React 輕量狀態(tài)管理Unstated

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

    Profeel 評論0 收藏0
  • React組件設(shè)計(jì)實(shí)踐總結(jié)05 - 狀態(tài)管理

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

    ideaa 評論0 收藏0
  • 讀zent源碼庫之Dialog組件實(shí)現(xiàn)

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

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

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

    DobbyKim 評論0 收藏0
  • React 新 Context API 在前端狀態(tài)管理的實(shí)踐

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

    wing324 評論0 收藏0

發(fā)表評論

0條評論

FrancisSoung

|高級講師

TA的文章

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