摘要:返回,用來包裹頂層組件,向應(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é)合React-redux
Mobx結(jié)合mobx-react
React 的新的Context api本質(zhì)上并不是React或者M(jìn)box這種狀態(tài)管理工具的替代品,充其量只是對(duì)React
自身狀態(tài)管理短板的補(bǔ)充。而Redux和Mbox這兩個(gè)庫本身并不是為React設(shè)計(jì)的,對(duì)于一些小型的React應(yīng)用
比較重。
Unstated是基于context API,也就是使用React.createContext()來創(chuàng)建一個(gè)StateContext來傳遞狀態(tài)的庫
Container:狀態(tài)管理類,內(nèi)部使用state存儲(chǔ)狀態(tài),通過setState實(shí)現(xiàn)狀態(tài)的更新,api設(shè)計(jì)與React的組件基本一致。
Provider:返回Provider,用來包裹頂層組件,向應(yīng)用中注入狀態(tài)管理實(shí)例,可做數(shù)據(jù)的初始化。
Subscribe:本質(zhì)上是Consumer,獲取狀態(tài)管理實(shí)例,在Container實(shí)例更新狀態(tài)的時(shí)候強(qiáng)制更新視圖。
簡(jiǎn)單的例子我們拿最通用的計(jì)數(shù)器的例子來看unstated如何使用,先明確一下結(jié)構(gòu):Parent作為父組件包含兩個(gè)子組件:Child1和Child2。
Child1展示數(shù)字,Child2操作數(shù)字的加減。然后,Parent組件的外層會(huì)包裹一個(gè)根組件。
首先,共享狀態(tài)需要有個(gè)狀態(tài)管理的地方,與Redux的Reducer不同的是,Unstated是通過一個(gè)繼承自Container實(shí)例:
import { Container } from "unstated"; class CounterContainer extends Container { constructor(initCount) { super(...arguments); this.state = {count: initCount || 0}; } increment = () => { this.setState({ count: this.state.count + 1 }); } decrement = () => { this.setState({ count: this.state.count - 1 }); } } export default CounterContainer
看上去是不是很熟悉?像一個(gè)React組件類。CounterContainer繼承自Unstated暴露出來的Container類,利用state存儲(chǔ)數(shù)據(jù),setState維護(hù)狀態(tài),
并且setState與React的setState用法一致,可傳入函數(shù)。返回的是一個(gè)promise。
來看一下要顯示數(shù)字的Child1組件,利用Subscribe與CounterContainer建立聯(lián)系。
import React from "react" import { Subscribe } from "unstated" import CounterContainer from "./store/Counter" class Child1 extends React.Component { render() { return{ counter => { return } } export default Child1{counter.state.count}} }
再來看一下要控制數(shù)字加減的Child2組件:
import React from "react" import { Button } from "antd" import { Subscribe } from "unstated" import CounterContainer from "./store/Counter" class Child2 extends React.Component { render() { return{ counter => { return } } export default Child2} }
Subscribe內(nèi)部返回的是StateContext.Consumer,通過to這個(gè)prop關(guān)聯(lián)到CounterContainer實(shí)例,
使用renderProps模式渲染視圖,Subscribe之內(nèi)調(diào)用的函數(shù)的參數(shù)就是訂閱的那個(gè)狀態(tài)管理實(shí)例。
Child1與Child2通過Subscribe訂閱共同的狀態(tài)管理實(shí)例CounterContainer,所以Child2可以調(diào)用
CounterContainer之內(nèi)的increment和decrement方法來更新狀態(tài),而Child1會(huì)根據(jù)更新來顯示數(shù)據(jù)。
看一下父組件Parent
import React from "react" import { Provider } from "unstated" import Child1 from "./Child1" import Child2 from "./Child2" import CounterContainer from "./store/Counter" const counter = new CounterContainer(123) class Parent extends React.Component { render() { return父組件 } } export default Parent
Provider返回的是StateContext.Provider,Parent通過Provider向組件的上下文中注入狀態(tài)管理實(shí)例。
這里,可以不注入實(shí)例。不注入的話,Subscribe內(nèi)部就不能拿到注入的實(shí)例去初始化數(shù)據(jù),也就是給狀態(tài)一個(gè)默認(rèn)值,比如上邊我給的是123。
也可以注入多個(gè)實(shí)例:
{/*Components*}
那么,在Subscribe的時(shí)候可以拿到多個(gè)實(shí)例。
分析原理{count1, count2) => {}
弄明白原理之前需要先明白Unstated提供的三個(gè)API之間的關(guān)系。我根據(jù)自己的理解,畫了一張圖:
來梳理一下整個(gè)流程:
創(chuàng)建狀態(tài)管理類繼承自Container
生成上下文,new一個(gè)狀態(tài)管理的實(shí)例,給出默認(rèn)值,注入Provider
Subscribe訂閱狀態(tài)管理類。內(nèi)部通過_createInstances方法來初始化狀態(tài)管理實(shí)例并訂閱該實(shí)例,具體過程如下:
從上下文中獲取狀態(tài)管理實(shí)例,如果獲取到了,那它直接去初始化數(shù)據(jù),如果沒有獲取到
那么就用to中傳入的狀態(tài)管理類來初始化實(shí)例。
將自身的更新視圖的函數(shù)onUpdate通過訂閱到狀態(tài)管理實(shí)例,來實(shí)現(xiàn)實(shí)例內(nèi)部setState的時(shí)候,調(diào)用onUpdate更新視圖。
_createInstances方法返回創(chuàng)建的狀態(tài)管理實(shí)例,作為參數(shù)傳遞給renderProps調(diào)用的函數(shù),函數(shù)拿到實(shí)例,操作或顯示數(shù)據(jù)。
Container用來實(shí)現(xiàn)一個(gè)狀態(tài)管理類。可以理解為redux中action和reducer的結(jié)合。概念相似,但實(shí)現(xiàn)不同。來看一下Container的源碼
export class Container { constructor() { CONTAINER_DEBUG_CALLBACKS.forEach(cb => cb(this)); this.state = null; this.listeners = []; } setState(updater, callback) { return Promise.resolve().then(() => { let nextState = null; if (typeof updater === "function") { nextState = updater(this.state); } else { nextState = updater; } if (nextState === null) { callback && callback(); } // 返回一個(gè)新的state this.state = Object.assign({}, this.state, nextState); // 執(zhí)行l(wèi)istener,也就是Subscribe的onUpdate函數(shù),用來強(qiáng)制刷新視圖 const promises = this.listeners.map(listener => listener()); return Promise.all(promises).then(() => { if (callback) { return callback(); } }); }); } subscribe(fn) { this.listeners.push(fn); } unsubscribe(fn) { this.listeners = this.listeners.filter(f => f !== fn); } }
Container包含了state、listeners,以及setState、subscribe、unsubscribe這三個(gè)方法。
state來存放數(shù)據(jù),listeners是一個(gè)數(shù)組,存放更新視圖的函數(shù)。
subscribe會(huì)將更新的函數(shù)(Subscribe組件內(nèi)的onUpdate)放入linsteners。
setState和react的setState相似。執(zhí)行時(shí),會(huì)根據(jù)變動(dòng)返回一個(gè)新的state,
同時(shí)循環(huán)listeners調(diào)用其中的更新函數(shù)。達(dá)到更新頁面的效果。
unsubscribe用來取消訂閱。
ProviderProvider本質(zhì)上返回的是StateContext.Provider。
export function Provider(ProviderProps) { return ({parentMap => { let childMap = new Map(parentMap); if (props.inject) { props.inject.forEach(instance => { childMap.set(instance.constructor, instance); }); } return ( ); }{props.children} ); }}
它自己接收一個(gè)inject屬性,經(jīng)過處理后,將它作為context的值傳入到上下文環(huán)境中。
可以看出,傳入的值為一個(gè)map,使用Container類作為鍵,Container類的實(shí)例作為值。
Subscribe會(huì)接收這個(gè)map,優(yōu)先使用它來實(shí)例化Container類,初始化數(shù)據(jù)。
可能有人注意到了Provider不是直接返回的StateContext.Provider,而是套了一層
StateContext.Consumer。這樣做的目的是Provider之內(nèi)還可以嵌套Provider。
內(nèi)層Provider的value可以繼承自外層。
簡(jiǎn)單來說就是連接組件與狀態(tài)管理類的一座橋梁,可以想象成react-redux中connect的作用
class Subscribe extends React.Component { constructor(props) { super(props); this.state = {}; this.instances = []; this.unmounted = false; } componentWillUnmount() { this.unmounted = true; this.unsubscribe(); } unsubscribe() { this.instances.forEach((container) => { container.unsubscribe(this.onUpdate); }); } onUpdate = () => new Promise((resolve) => { if (!this.unmounted) { this.setState(DUMMY_STATE, resolve); } else { resolve(); } }) _createInstances(map, containers) { this.unsubscribe(); if (map === null) { throw new Error("You must wrap yourcomponents with a "); } const safeMap = map; const instances = containers.map((ContainerItem) => { let instance; if ( typeof ContainerItem === "object" && ContainerItem instanceof Container ) { instance = ContainerItem; } else { instance = safeMap.get(ContainerItem); if (!instance) { instance = new ContainerItem(); safeMap.set(ContainerItem, instance); } } instance.unsubscribe(this.onUpdate); instance.subscribe(this.onUpdate); return instance; }); this.instances = instances; return instances; } render() { return ( { map => this.props.children.apply( null, this._createInstances(map, this.props.to), ) } ); } }
這里比較重要的是_createInstances與onUpdate兩個(gè)方法。StateContext.Consumer接收Provider傳遞過來的map,
與props接收的to一并傳給_createInstances。
onUpdate:沒有做什么其他事情,只是利用setState更新視圖,返回一個(gè)promise。它存在的意義是在訂閱的時(shí)候,
作為參數(shù)傳入Container類的subscribe,擴(kuò)充Container類的listeners數(shù)組,隨后在Container類setState改變狀態(tài)以后,
循環(huán)listeners的每一項(xiàng)就是這個(gè)onUpdate方法,它執(zhí)行,就會(huì)更新視圖。
_createInstances: map為provider中inject的狀態(tài)管理實(shí)例數(shù)據(jù)。如果inject了,那么就用map來實(shí)例化數(shù)據(jù),
否則用this.props.to的狀態(tài)管理類來實(shí)例化。之后調(diào)用instance.subscribe方法(也就是Container中的subscribe),
傳入自身的onUpdate,實(shí)現(xiàn)訂閱。它存在的意義是實(shí)例化Container類并將自身的onUpdate訂閱到Container類實(shí)例,
最終返回這個(gè)Container類的實(shí)例,作為this.props.children的參數(shù)并進(jìn)行調(diào)用,所以在組件內(nèi)部可以進(jìn)行類似這樣的操作:
總結(jié){ counter => { return } }
Unstated上手很容易,理解源碼也不難。重點(diǎn)在于理解發(fā)布(Container類),Subscribe組件實(shí)現(xiàn)訂閱的思路。
其API的設(shè)計(jì)貼合React的設(shè)計(jì)理念。也就是想要改變UI必須setState。另外可以不用像Redux一樣寫很多樣板代碼。
理解源碼的過程中受到了下面兩篇文章的啟發(fā),衷心感謝:
純粹極簡(jiǎn)的react狀態(tài)管理組件unstated
Unstated淺析
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/103345.html
摘要:在該版本發(fā)布之后,開發(fā)團(tuán)隊(duì)并不會(huì)繼續(xù)發(fā)布新的特性,而會(huì)著眼于進(jìn)行重大的錯(cuò)誤修復(fù)。發(fā)布每六個(gè)星期,團(tuán)隊(duì)就會(huì)創(chuàng)建新的分支作為發(fā)布通道,本文即是對(duì)新近發(fā)布的版本進(jìn)行簡(jiǎn)要介紹。 showImg(https://segmentfault.com/img/remote/1460000013229009); 前端每周清單專注前端領(lǐng)域內(nèi)容,以對(duì)外文資料的搜集為主,幫助開發(fā)者了解一周前端熱點(diǎn);分為新聞熱...
摘要:要求通過要求數(shù)據(jù)變更函數(shù)使用裝飾或放在函數(shù)中,目的就是讓狀態(tài)的變更根據(jù)可預(yù)測(cè)性單向數(shù)據(jù)流。同一份數(shù)據(jù)需要響應(yīng)到多個(gè)視圖,且被多個(gè)視圖進(jìn)行變更需要維護(hù)全局狀態(tài),并在他們變動(dòng)時(shí)響應(yīng)到視圖數(shù)據(jù)流變得復(fù)雜,組件本身已經(jīng)無法駕馭。今天是 520,這是本系列最后一篇文章,主要涵蓋 React 狀態(tài)管理的相關(guān)方案。 前幾篇文章在掘金首發(fā)基本石沉大海, 沒什么閱讀量. 可能是文章篇幅太長了?掘金值太低了? ...
摘要:此處繼承了上面的可以注入現(xiàn)成的狀態(tài)管理實(shí)例,添加到之中。返回值寫成的意義簡(jiǎn)單一句話概括,這么寫可以避免改變導(dǎo)致子組件的重復(fù)渲染。就是創(chuàng)建狀態(tài)管理組件時(shí)默認(rèn)傳遞的監(jiān)聽函數(shù),用的是的更新一個(gè)空對(duì)象。返回值寫成的意義。 簡(jiǎn)介 unstated是一個(gè)極簡(jiǎn)的狀態(tài)管理組件 看它的簡(jiǎn)介:State so simple, it goes without saying 對(duì)比 對(duì)比redux: 更加靈活...
摘要:本文轉(zhuǎn)載至今日頭條技術(shù)博客眾所周知,的單向數(shù)據(jù)流模式導(dǎo)致狀態(tài)只能一級(jí)一級(jí)的由父組件傳遞到子組件,在大中型應(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)...
摘要:只是抱怨事物的狀態(tài)并沒有什么卵用,我打算給你一個(gè)實(shí)實(shí)在在的一步一步征服生態(tài)圈的學(xué)習(xí)計(jì)劃。好消息是,這剛好是本學(xué)習(xí)計(jì)劃關(guān)注的問題。比如,一個(gè)不錯(cuò)的出發(fā)點(diǎn)是的課。是一個(gè)由創(chuàng)建和開源的庫。我個(gè)人推薦的初學(xué)者課程。而個(gè)人項(xiàng)目是嘗試新技術(shù)的完美時(shí)機(jī)。 本文轉(zhuǎn)載自:眾成翻譯譯者:網(wǎng)絡(luò)埋伏紀(jì)事鏈接:http://www.zcfy.cc/article/1617原文:https://medium.fr...
閱讀 3121·2021-11-24 09:39
閱讀 981·2021-09-07 10:20
閱讀 2402·2021-08-23 09:45
閱讀 2278·2021-08-05 10:00
閱讀 579·2019-08-29 16:36
閱讀 842·2019-08-29 11:12
閱讀 2826·2019-08-26 11:34
閱讀 1847·2019-08-26 10:56