使用RxJS管理React应用状态的介绍

本篇文章给大家带来的内容是关于使用rxjs管理react应用状态的介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

随着前端应用的复杂度越来越高,如何管理应用的数据已经是一个不可回避的问题。当你面对的是业务场景复杂、需求变动频繁、各种应用数据互相关联依赖的大型前端应用时,你会如何去管理应用的状态数据呢?

我们认为应用的数据大体上可以分为四类:

事件:瞬间产生的数据,数据被消费后立即销毁,不存储。异步:异步获取的数据;类似于事件,是瞬间数据,不存储。状态:随着时间空间变化的数据,始终会存储一个当前值/最新值。常量:固定不变的数据。

RxJS天生就适合编写异步和基于事件的程序,那么状态数据用什么去管理呢?还是用RxJS吗? 合不合适呢?

我们去调研和学习了前端社区已有的优秀的状态管理解决方案,也从一些大牛分享的关于用RxJS设计数据层的构想和实践中得到了启发:

使用RxJS完全可以实现诸如Redux,Mobx等管理状态数据的功能。应用的数据不是只有状态的,还有事件、异步、常量等等。如果整个应用都由observable来表达,则可以借助RxJS基于序列且可响应的的特性,以流的方式自由地拼接和组合各种类型的数据,能够更优雅更高效地抽象出可复用可扩展的业务模型。

出于以上两点原因,最终决定基于RxJS来设计一套管理应用的状态的解决方案。

原理介绍

对于状态的定义,通常认为状态需要满足以下3个条件:

是一个具有多个值的集合。能够通过event或者action对值进行转换,从而得到新的值。有“当前值”的概念,对外一般只暴露当前值,即最新值。

那么,RxJS适合用来管理状态数据吗?答案是肯定的!

首先,因为Observable本身就是多个值的推送集合,所以第一个条件是满足的!

其次,我们可以实现一个使用dispatch action模式来推送数据的observable来满足第二个条件!

众所周知,RxJS中的observable可以分为两种类型:

cold observable: 推送值的生产者(producer)来自observable内部。

将会推送几个值以及推送什么样的值已在observable创建时被定义下来,不可改变。producer与观察者(observer) 是一对一的关系,即是单播的。每当有observer订阅时,producer都会把预先定义好的若干个值依次推送给observer。

hot observable: 推送值的producer来自observable外部。

将会推送几个值、推送什么样的值以及何时推送在创建时都是未知的。producer与observer是一对多的关系,即是多播的。每当有observer订阅时,会将observer注册到观察者列表中,类似于其他库或语言中的addListener的工作方式。当外部的producer被触发或执行时,会将值同时推送给所有的observer;也就是说,所有的observer共享了hot observable推送的值。

RxJS提供的BehaviorSubject就是一种特殊的hot observable,它向外暴露了推送数据的接口next函数;并且有“当前值”的概念,它保存了发送给observer的最新值,当有新的观察者订阅时,会立即从BehaviorSubject那接收到“当前值”。

那么这说明使用BehaviorSubject来更新状态并保存状态的当前值是可行的,第三个条件也满足了。

简单实现

请看以下的代码:

import { BehaviorSubject } from 'rxjs';// 数据推送的生产者class StateMachine {  constructor(subject, value) {    this.subject = subject;    this.value = value;  }  producer(action) {    let oldValue = this.value;    let newValue;    switch (action.type) {      case 'plus':        newValue = ++oldValue;        this.value = newValue;        this.subject.next(newValue);        break;      case 'toDouble':        newValue = oldValue * 2;        this.value = newValue;        this.subject.next(newValue);        break;    }  }}const value = 1;  // 状态的初始值const count$ = new BehaviorSubject(value);const stateMachine = new StateMachine(count$, value);// 派遣actionfunction dispatch(action) {  stateMachine.producer(action);}count$.subscribe(val => {  console.log(val);});setTimeout(() => {  dispatch({    type: "plus"  });}, 1000);setTimeout(() => {  dispatch({    type: "toDouble"  });}, 2000);

登录后复制

执行代码控制台会打印出三个值:

Console 1 2 4

登录后复制

上面的代码简单实现了一个简单管理状态的例子:

状态的初始值: 1执行plus之后的状态值: 2执行toDouble之后的状态值: 4

实现方法挺简单的,就是使用BehaviorSubject来表达状态的当前值:

第一步,通过调用dispatch函数使producer函数执行第二部,producer函数在内部调用了BehaviorSubject的next函数,推送了新数据,BehaviorSubject的当前值更新了,也就是状态更新了。

不过写起来略微繁琐,我们对其进行了封装,优化后写法见下文。

使用操作符来创建状态数据

我们自定义了一个操作符state用来创建一个能够通过dispatch action模式推送新数据的BehaviorSubject,我们称她为stateObservable。

const count$ = state({  // 状态的唯一标识名称  name: "count",      // 状态的默认值  defaultValue: 1,      // 数据推送的生产者函数  producer(next, value, action) {    switch (action.type) {      case "plus":        next(value + 1);        break;      case "toDouble":        next(value * 2);        break;    }  }});

登录后复制

更新状态

在你想要的任意位置使用函数dispatch派遣action即可更新状态!

dispatch("count", {  type: "plus"})

登录后复制

异步数据

RxJS的一大优势就在于能够统一同步和异步,使用observable处理数据你不需要关注同步还是异步。

下面的例子我们使用操作符from将promise转换为observable。

指定observable作为状态的初始值(首次推送数据)

const todos$ = state({  name: "todos",      // `observable`推送的数据将作为状态的初始值  initial: from(getAsyncData())      //...  });

登录后复制

producer推送observable

const todos$ = state({  name: "todos",      defaultValue: []      // 数据推送的生产者函数  producer(next, value, action) {    switch (action.type) {      case "getAsyncData":        next(          from(getAsyncData())        );        break;    }  }});

登录后复制

执行getAsyncData之后,from(getAsyncData())的推送数据将成为状态的最新值。

衍生状态

由于状态todos$是一个observable,所以可以很自然地使用RxJS操作符转换得到另一个新的observable。并且这个observable的推送来自todos$;也就是说只要todos$推送新数据,它也会推送;效果类似于Vue的计算属性。

// 未完成任务数量const undoneCount$ = todos$.pipe(  map(todos => {    let _conut = 0;    todos.forEach(item => {      if (!item.check) ++_conut;    });    return _conut;  }));

登录后复制

React视图渲染

我们可能会在组件的生命周期内订阅observable得到数据渲染视图。

class Todos extends React.Component {  componentWillMount() {    todos$.subscribe(data => {      this.setState({        todos: data      });    });  }}

登录后复制

我们可以再优化下,利用高阶组件封装一个装饰器函数@subscription,顾名思义,就是为React组件订阅observable以响应推送数据的变化;它会将observable推送的数据转换为React组件的props。

@subscription({  todos: todos$})class TodoList extends React.Component {  render() {    return (      

任务列表

{this.props.todos.map((item, n) => { return ; })}

); }}

登录后复制

总结

使用RxJS越久,越令人受益匪浅。

因为它基于observable序列提供了较高层次的抽象,并且是观察者模式,可以尽可能地减少各组件各模块之间的耦合度,大大减轻了定位BUG和重构的负担。因为是基于observable序列来编写代码的,所以遇到复杂的业务场景,总能按照一定的顺序使用observable描述出来,代码的可读性很强。并且当需求变动时,我可能只需要调整下observable的顺序,或者加个操作符就行了。再也不必因为一个复杂的业务流程改动了,需要去改好几个地方的代码(而且还容易改出BUG,笑~)。

所以,以上基于RxJS的状态管理方案,对我们来说是一个必需品,因为我们项目中大量使用了RxJS,如果状态数据也是observable,对我们抽象可复用可扩展的业务模型是一个非常大的助力。当然了,如果你的项目中没有使用RxJS,也许Redux和Mobx是更合适的选择。

这套基于RxJS的状态管理方案,我们已经用于开发公司的商用项目,反馈还不错。所以我们决定把这套方案整理成一个js lib,取名为:Floway,并在github上开源:

github源码:https://github.com/shayeLee/floway使用文档:https://shayelee.github.io/floway

【相关推荐:react视频教程】

以上就是使用RxJS管理React应用状态的介绍的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/2731657.html

(0)
上一篇 2025年3月8日 00:30:32
下一篇 2025年3月8日 00:30:38

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • es6 filter() 数组过滤的方法小结(附代码)

    本篇文章给大家带来的内容是关于es6 filter() 数组过滤的方法小结(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 Array.every(x=>x)是每一个都要满足 Array.some(x=&gt…

    编程技术 2025年3月8日
    000
  • JavaScript作用域的全面解析(附代码)

    本篇文章给大家带来的内容是关于javascript作用域的全面解析(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 作用域决定了变量的生命周期和可见性,变量在作用域范围之外是不可见的。 JavaScript 的作用…

    编程技术 2025年3月8日
    200
  • 基于 jQuery的键盘事件监听控件的介绍(代码示例)

    本篇文章给大家带来的内容是关于基于 jquery的键盘事件监听控件的介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 最近项目里要做一个画板,需要对键盘事件进行监听,来进行诸如撤回、重做、移动、缩放等操作,因…

    编程技术 2025年3月8日
    200
  • JavaScript创建对象(3种方式)

    在语法方面,javascript是一种灵活的面向对象语言。在本文中,我们将给大家介绍javascript实例化对象的不同方法。 需要注意的是JavaScript是一种无类语言,并且函数以某种方式使用,以便它们模拟一个类。(推荐:《javas…

    编程技术 2025年3月8日
    200
  • Vue组件内部实现一个双向数据绑定的代码示例

    本篇文章给大家带来的内容是关于Vue组件内部实现一个双向数据绑定的代码示例,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 思路:父组件通过props传值给子组件,子组件通过 $emit 来通知父组件修改相应的props值,…

    编程技术 2025年3月8日
    200
  • JavaScript常用数组操作的介绍(代码示例)

    本篇文章给大家带来的内容是关于javascript常用数组操作的介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 在日常的开发中离不开前端对数据的处理,在这里整理下ES6/ES7/ES8…新的数组…

    编程技术 2025年3月8日
    200
  • ReactDom.render的详细解析

    本篇文章给大家带来的内容是关于ReactDom.render的详细解析,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 步骤 1.创建ReactRoot 2.创建FiberRoot和FiberRoot 3.创建更新 rend…

    编程技术 2025年3月8日
    200
  • JavaScript中错误异常的分析(附示例)

    本篇文章给大家带来的内容是关于JavaScript中错误异常的分析(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 我的建议是不要隐藏错误,勇敢地抛出来。没有人会因为代码出现 bug 导致程序崩溃而羞耻,我们可以让…

    编程技术 2025年3月8日
    200
  • Generator函数与async函数的区别介绍

    本篇文章给大家带来的内容是关于Generator函数与async函数的区别介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 Generator函数: Generator函数是ES2015提供的异步解决方案,与普通函数有很…

    编程技术 2025年3月8日
    200
  • React表单元素的用法介绍(附代码)

    本篇文章给大家带来的内容是关于React表单元素的用法介绍(附代码),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。 今天来讲讲react的表单元素。 受控元素 下面来看一下如何获取输入框的值 import React, {…

    编程技术 2025年3月8日
    200

发表回复

登录后才能评论