• 技术文章 >web前端 >js教程

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

    不言不言2019-04-03 10:24:59转载1221
    本篇文章给大家带来的内容是关于使用RxJS管理React应用状态的介绍,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

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

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

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

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

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

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

    原理介绍

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

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

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

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

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

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

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

    hot observable: 推送值的producer来自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);
    
    // 派遣action
    function 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

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

    实现方法挺简单的,就是使用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处理数据你不需要关注同步还是异步。

    下面的例子我们使用操作符frompromise转换为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 (
          <p className="todolist">
            <h1 className="header">任务列表</h1>
            {this.props.todos.map((item, n) => {
              return <TodoItem item={item} key={item.desc} />;
            })}
          </p>
        );
      }
    }

    总结

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

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

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

    【相关推荐:react视频教程

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

    声明:本文转载于:segmentfault,如有侵犯,请联系admin@php.cn删除
    上一篇:es6 filter() 数组过滤的方法小结(附代码) 下一篇:JavaScript作用域的全面解析(附代码)
    大前端线上培训班

    相关文章推荐

    • React高阶组件(装饰器)的介绍(代码示例)• react的setSate的异步问题的分析• React Hooks中如何请求数据(详解)• react中使用css的七种方法介绍(附代码)

    全部评论我要评论

  • 取消发布评论发送
  • 1/1

    PHP中文网