热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

明白React轻量状况治理库Unstated

个人网站:https:www.neroht.com在React写运用的时刻,不免碰到跨组件通讯的题目。如今已经有许多的解决方案。React本身的ContextRedux连系Reac

个人网站: https://www.neroht.com

在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存储状况,经由历程setState完成状况的更新,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.setState({ count: this.state.count + 1 });
}
decrement = () => {
this.setState({ count: this.state.count - 1 });
}
}
export default CounterContainer

看上去是否是很熟悉?像一个React组件类。CounterContainer继续自Unstated暴露出来的Container类,应用state存储数据,setState保护状况,
而且setState与React的setState用法一致,可传入函数。返回的是一个promise。

同享状况

来看一下要显现数字的Child1组件,应用Subscribe与CounterContainer竖立联络。

import React from 'react'
import { Subscribe } from 'unstated'
import CounterContainer from './store/Counter'
class Child1 extends React.Component {
render() {
return
{
counter => {
return

{counter.state.count}

}
}

}
}
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
{
counter => {
return





}
}

}
}
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
父组件



}
}
export default Parent

Provider返回的是StateContext.Provider,Parent经由历程Provider向组件的高低文中注入状况治理实例。
这里,能够不注入实例。不注入的话,Subscribe内部就不能拿到注入的实例去初始化数据,也就是给状况一个默认值,比方上边我给的是123。

也能够注入多个实例:


{/*Components*}

那末,在Subscribe的时刻能够拿到多个实例。


{count1, count2) => {}

剖析道理

弄邃晓道理之前须要先邃晓Unstated供应的三个API之间的关联。我依据本身的明白,画了一张图:

《明白 React 轻量状况治理库 Unstated》

来梳理一下全部流程:

  1. 竖立状况治理类继续自Container
  2. 天生高低文,new一个状况治理的实例,给出默认值,注入Provider
  3. Subscribe定阅状况治理类。内部经由历程_createInstances要领来初始化状况治理实例并定阅该实例,详细历程以下:
  • 从高低文中猎取状况治理实例,假如猎取到了,那它直接去初始化数据,假如没有猎取到

那末就用to中传入的状况治理类来初始化实例。

  • 将本身的更新视图的函数onUpdate经由历程定阅到状况治理实例,来完成实例内部setState的时刻,挪用onUpdate更新视图。
  • _createInstances要领返回竖立的状况治理实例,作为参数通报给renderProps挪用的函数,函数拿到实例,操纵或显现数据。

Container

用来完成一个状况治理类。能够明白为redux中action和reducer的连系。观点相似,但完成差别。来看一下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();
}
// 返回一个新的state
this.state = Object.assign({}, this.state, nextState);
// 实行listener,也就是Subscribe的onUpdate函数,用来强迫革新视图
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这三个要领。

  • state来寄存数据,listeners是一个数组,寄存更新视图的函数。
  • subscribe会将更新的函数(Subscribe组件内的onUpdate)放入linsteners。
  • setState和react的setState相似。实行时,会依据更改返回一个新的state,

同时轮回listeners挪用个中的更新函数。到达更新页面的结果。

  • unsubscribe用来作废定阅。

Provider

Provider本质上返回的是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}

);
}}

);
}

它本身吸收一个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 = 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 your components 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两个要领。StateContext.Consumer吸收Provider通报过来的map,
与props吸收的to一并传给_createInstances。

onUpdate:没有做什么其他事变,只是应用setState更新视图,返回一个promise。它存在的意义是在定阅的时刻,
作为参数传入Container类的subscribe,扩大Container类的listeners数组,随后在Container类setState转变状况今后,
轮回listeners的每一项就是这个onUpdate要领,它实行,就会更新视图。

_createInstances: map为provider中inject的状况治理实例数据。假如inject了,那末就用map来实例化数据,
否则用this.props.to的状况治理类来实例化。以后挪用instance.subscribe要领(也就是Container中的subscribe),
传入本身的onUpdate,完成定阅。它存在的意义是实例化Container类并将本身的onUpdate定阅到Container类实例,
终究返回这个Container类的实例,作为this.props.children的参数并举行挪用,所以在组件内部能够举行相似如许的操纵:


{
counter => {
return





}
}

总结

Unstated上手很轻易,明白源码也不难。重点在于明白宣布(Container类),Subscribe组件完成定阅的思绪。
其API的设想贴合React的设想理念。也就是想要转变UI必需setState。别的能够不必像Redux一样写许多榜样代码。

明白源码的历程当中受到了下面两篇文章的启示,衷心感谢:

地道极简的react状况治理组件unstated

Unstated浅析


推荐阅读
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文总结了在编写JS代码时,不同浏览器间的兼容性差异,并提供了相应的解决方法。其中包括阻止默认事件的代码示例和猎取兄弟节点的函数。这些方法可以帮助开发者在不同浏览器上实现一致的功能。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 本文介绍了闭包的定义和运转机制,重点解释了闭包如何能够接触外部函数的作用域中的变量。通过词法作用域的查找规则,闭包可以访问外部函数的作用域。同时还提到了闭包的作用和影响。 ... [详细]
  • VScode格式化文档换行或不换行的设置方法
    本文介绍了在VScode中设置格式化文档换行或不换行的方法,包括使用插件和修改settings.json文件的内容。详细步骤为:找到settings.json文件,将其中的代码替换为指定的代码。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • 原文地址:https:www.cnblogs.combaoyipSpringBoot_YML.html1.在springboot中,有两种配置文件,一种 ... [详细]
  • 本文介绍了在Mac上搭建php环境后无法使用localhost连接mysql的问题,并通过将localhost替换为127.0.0.1或本机IP解决了该问题。文章解释了localhost和127.0.0.1的区别,指出了使用socket方式连接导致连接失败的原因。此外,还提供了相关链接供读者深入了解。 ... [详细]
  • 展开全部下面的代码是创建一个立方体Thisexamplescreatesanddisplaysasimplebox.#Thefirstlineloadstheinit_disp ... [详细]
  • 本文介绍了作者在开发过程中遇到的问题,即播放框架内容安全策略设置不起作用的错误。作者通过使用编译时依赖注入的方式解决了这个问题,并分享了解决方案。文章详细描述了问题的出现情况、错误输出内容以及解决方案的具体步骤。如果你也遇到了类似的问题,本文可能对你有一定的参考价值。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
  • 从零基础到精通的前台学习路线
    随着互联网的发展,前台开发工程师成为市场上非常抢手的人才。本文介绍了从零基础到精通前台开发的学习路线,包括学习HTML、CSS、JavaScript等基础知识和常用工具的使用。通过循序渐进的学习,可以掌握前台开发的基本技能,并有能力找到一份月薪8000以上的工作。 ... [详细]
  • jQuery实现简单的动画效果及用法详解
    本文详细介绍了使用jQuery实现简单动画效果的方法,包括显示/隐藏、向上收缩/向下展开、淡入/淡出、自定义动画等。同时提供了具体的用法示例,并解释了参数的含义和使用技巧。通过本文的学习,读者可以掌握如何使用jQuery实现各种动画效果,为网页增添生动和互动性。 ... [详细]
  • Matlab 中的一些小技巧(2)
    1.Ctrl+D打开子程序  在MATLAB的Editor中,将输入光标放到一个子程序名称中间,然后按Ctrl+D可以打开该子函数的m文件。当然这个子程序要在路径列表中(或在当前工作路径中)。实际上 ... [详细]
author-avatar
潮流时候男装
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有