首页 > 作文

理解React轻量状态管理库Unstated

更新时间:2023-04-03 13:58:27 阅读: 评论:0

在react写应用的时候,难免遇到跨通信的问题。现在已经有很多的解决方案。

react本身的context redux结合react-redux mobx结合mobx-react

react 的新的context api本质上并不是react或者mbox这种状态管理工具的替代品,充其量只是对react
自身状态管理短板的补充。而redux和mbox这两个库本身并不是为react设计的,对于一些小型的react应用
比较重。

基本概念

unstated是基于context api。也就是使用react.createcontext()创建一个statecontext来传递状态,

container:状态管理类,内部使用state存储状态,通过tstate实现状态的更新,api设计与react的组件基本一致。 provider:返回provider,用来包裹顶层组件,向应用中注入状态管理实例,可做数据的初始化。 subscribe:本质上是consumer,获取状态管理实例,在container实例更新状态的时候强制更新视图。

简单的例子

我们拿最通用的计数器的例子来看unstated如何使用,先明确一下结构:parent作为父组件包含两个子组件:child1和child2。
child1展示数字,child2操作数字的加减。然后,parent组件的外层会包裹一个根组件。

维护状态

首先,共享状态需要有个状态管理的地方,与redux的reducer不同的是,unstated是通过一个继承自container实例:

import { container } from 'unstated';class countercontainer extends container {  constructor(initcount) {    super(...arguments);    this.state = {count: initcount || 0};  }  increment = () => {    this.tstate({ count: this.state.count + 1 });  }  decrement = () => {    this.tstate({ count: this.state.count - 1 });  }}export default countercontainer

看上去是不是很熟悉?像一个react组件类。countercontainer继承自unstated暴露出来的container类,利用state存储数据,tstate维护状态,
并且tstate与react的tstate用法一致,可传入函数。返回的是一个promi。

共享状态

来看一下要显示数字的child1组件,利用subscribe与countercontainer建立联系。

import react from 'react'import { subscribe } from 'unstated'import countercontainer from './store/counter'class child1 ex文言文是什么意思tends react.component {  render() {    return <subscribe to={[countercontainer]}>      {        counter => {          return <p>{counter.state.count}</p>        }      }    </subscribe>  }}export default child1

再来看一下要控制数字加减的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 <subscribe to={[countercontainer]}>      {        counter => {          return <p>            <button onclick={counter.increment}>增加</button>            <button onclick={counter.decrement}>减少</button>          </p>        }      }    </subscribe>  }}export default child2

subscribe内部返回的是statecontext.consumer,通过to这个prop关联到countercontainer实例,
使用renderprops模式渲染视图,subscribe之内调用的函数的参数就是订阅的那个状态管理实例。
child1child2通过subscribe订阅共同的状态管理实例countercontainer,所以child2可以调用
countercontainer之内的increment和decrement方法来更新状态,而child1会根据更新来显示数据。

看一下父组件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 <provider inject={[counter]}>      父组件      <child1/>      <child2/>    </provider>  }}export default parent

provider返回的是statecontext.provider,parent通过provider向组件的上下文中注入状态管理实例。
这里,可以不注入实例。不注入的话,subscribe内部就不能拿到注入的实例去初始化数据,也就是给状态一个默认值,比如上边我给的是123。

也可以注入多个实例:

<provider inject={[count1, count2]}>   {/*components*}</provide>

那么,在subscribe的时候可以拿到多个实例。

<subscribe to={[countercontainer1, countercontainer2]}>  {count1, count2) => {}</subscribe>

分析原理

弄明白原理之前需要先明白unstated提供的三个api之间的关系。我根据自己的理解,画了一张图:

来梳理一下整个流程:

创建状态管理类继承自container 生成上下文,new一个状态管理的实例,给出默认值,注入provider subscribe订阅状态管理类。内部通过_createinstances方法来初始化状态管理实例并订阅该实例,具体过程如下: 从上下文中获取状态管理实例,如果获取到了,那它直接去初始化数据,如果没有获取到

那么就用to中传入的状态管理类来初始化实例。

将自身的更新视图的函数onupdate通过订阅到状态管理实例,来实现实例内部tstate的时候,调用onupdate更新视图。 _createinstances方法返回创建的状态管理实例,作为参数传递给renderprops调用的函数,函数拿到实例,操作或显示数据。

container

用来实现一个状态管理类。可以理解为redux中action和reducer的结合。概念相似,但实现不同。来看一下container的

export class container {  constructor() {    container_debug_callbacks.foreach(cb => cb(this));    this.state = null;    this.listeners = [];  }  tstate(updater, callback) {    return promi.resolve().then(() => {      let nextstate = null;      if (typeof updater === 'function') {        nextstate = updater(this.state)里氏震级;      } el {        nextstate = updater;      }      if (nextstate === null) {        callback &&唐诗五言绝句; callback();      }      // 返回一个新的state      this.state = object.assign({}, this.state, nextstate);      // 执行listener,也就是subscribe的onupdate函数,用来强制刷新视图      const promis = this.listeners.map(listener => listener());      return promi.all(promis).then(() => {        if (callback) {          return callback();        }      });    });  }  subscribe(fn) {    this.listeners.push(fn);  }  unsubscribe(fn) {    this.listeners = this.listeners.filter(f => f !== fn);  }}

container包含了state、listeners,以及tstate、subscribe、unsubscribe这三个方法。

state来存放数据,listeners是一个数组,存放更新视图的函数。 subscribe会将更新的函数(subscribe组件内的onupdate)放入linsteners。 tstate和react的tstate相似。执行时,会根据变动返回一个新的state,

同时循环listeners调用其中的更新函数。达到更新页面的效果。

unsubscribe用来取消订阅。

provider

provider本质上返回的是statecontext.provider。

export function provider(providerprops) {  return (    <statecontext.consumer>      {parentmap => {        let childmap = new map(parentmap);        if (props.inject) {          props.inject.foreach(instance => {            childmap.t(instance.constructor, instance);          });        }        return (          <statecontext.provider value={childmap}>            {props.children}          </statecontext.provider>        );      }}    </statecontext.consumer>  );}

它自己接收一个inject属性,经过处理后,将它作为context的值传入到上下文环境中。
可以看出,传入的值为一个map,使用container类作为键,container类的实例作为值。
subscribe会接收这个map,优先使用它来实例化container类,初始化数据

可能有人注意到了provider不是直接返回的statecontext.provider,而是套了一层
statecontext.consumer。这样做的目的是provider之内还可以嵌套provider。
内层provider的value可以继承自外层。

subscribe

简单来说就是连接组件与状态管理类的一座桥梁,可以想象成react-redux中connect的作用

class subscribe extends react.component {  constructor(props) {    super(props);    this.state = {};    this.instances = [];    this.unmounted = fal;  }  componentwillunmount() {    this.unmounted = true;    this.unsubscribe();  }  unsubscribe() {    this.instances.foreach((container) => {      container.u男孩学什么舞蹈好nsubscribe(this.onupdate);    });  }  onupdate = () => new promi((resolve) => {    if (!this.unmounted) {      this.tstate(dummy_state, resolve);    } el {      resolve();    }  })  _createinstances(map, containers) {    this.unsubscribe();    if (map === null) {      throw new error('you must wrap your <subscribe> components with a <provider>');    }    const safemap = map;    const instances = containers.map((containeritem) => {      let instance;      if (        typeof containeritem === 'object' &&        containeritem instanceof container      ) {        instance = containeritem;      } el {        instance = safemap.get(containeritem);        if (!instance) {          instance = new con三八妇女节活动主题taineritem();          safemap.t(containeritem, instance);        }      }      instance.unsubscribe(this.onupdate);      instance.subscribe(this.onupdate);      return instance;    });    this.instances = instances;    return instances;  }  render() {    return (      <statecontext.consumer>        {          map => this.props.children.apply(            null,            this._createinstances(map, this.props.to),          )        }      </statecontext.consumer>    );  }}

这里比较重要的是_createinstances与onupdate两个方法。statecontext.consumer接收provider传递过来的map,
与props接收的to一并传给_createinstances。

onupdate:没有做什么其他事情,只是利用tstate更新视图,返回一个promi。它存在的意义是在订阅的时候,
作为参数传入container类的subscribe,扩充container类的listeners数组,随后在container类tstate改变状态以后,
循环listeners的每一项就是这个onupdate方法,它执行,就会更新视图。

_createinstances: map为provider中inject的状态管理实例数据。如果inject了,那么就用map来实例化数据,
否则用this.props.to的状态管理类来实例化。之后调用instance.subscribe方法(也就是container中的subscribe),
传入自身的onupdate,实现订阅。它存在的意义是实例化container类并将自身的onupdate订阅到container类实例,
最终返回这个container类的实例,作为this.props.children的参数并进行调用,所以在组件内部可以进行类似这样的操作:

 <subscribe to={[countercontainer]}>   {     counter => {       return <p>         <button onclick={counter.increment}>增加</button>         <button onclick={counter.decrement}>减少</button>       </p>     }   }</subscribe>

总结

unstated上手很容易,理解源码也不难。重点在于理解发布(container类),subscribe组件实现订阅的思路。
其api的设计贴合react的设计理念。也就是想要改变ui必须tstate。另外可以不用像redux一样写很多样板代码。

理解源码的过程中受到了下面两篇文章的启发,衷心感谢:

纯粹极简的react状态管理组件unstated

unstated浅析

本文发布于:2023-04-03 13:58:26,感谢您对本站的认可!

本文链接:https://www.wtabcd.cn/fanwen/zuowen/92f74dd2c7d2cb69b795de6395ae95c6.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

本文word下载地址:理解React轻量状态管理库Unstated.doc

本文 PDF 下载地址:理解React轻量状态管理库Unstated.pdf

标签:状态   实例   组件   的是
相关文章
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2022 Comsenz Inc.Powered by © 专利检索| 网站地图