Home  >  Article  >  Web Front-end  >  An introduction to using RxJS to manage React application state

An introduction to using RxJS to manage React application state

不言
不言forward
2019-04-03 10:24:592745browse

This article brings you an introduction to using RxJS to manage React application status. It has certain reference value. Friends in need can refer to it. I hope it will be helpful to you.

As front-end applications become more and more complex, how to manage application data has become an unavoidable problem. When you are faced with a large front-end application with complex business scenarios, frequent demand changes, and various application data that are interrelated and dependent, how will you manage the status data of the application? We believe that application data can be roughly divided into four categories:

Events: data generated in an instant. The data is destroyed immediately after being consumed and is not stored.
  • Asynchronous: Data obtained asynchronously; similar to events, it is instantaneous data and is not stored.
  • Status: Data that changes with time and space will always store a current value/latest value.
  • Constant: fixed data.
RxJS

is naturally suitable for writing asynchronous and event-based programs, so how to manage state data? Should I still use RxJS? Is it suitable? We investigated and learned about the excellent state management solutions already available in the front-end community, and also learned from the ideas and practices shared by some experts about using

RxJS

to design the data layer. Inspiration:

Using
    RxJS
  1. can completely implement functions such as Redux, Mobx, etc. to manage state data. The application data is not only status, but also events, asynchronous, constants, etc. If the entire application is expressed by
  2. observable
  3. , you can freely splice and combine various types of data in a streaming manner with the help of RxJS's sequence-based and responsive features. Abstract reusable and scalable business models more elegantly and efficiently.
  4. For the above two reasons, we finally decided to design a solution for managing application status based on
RxJS

. Principle Introduction

For the definition of state, it is generally believed that the state needs to meet the following three conditions:

is a set with multiple values.
  1. can convert the value through
  2. event
  3. or action to get a new value. There is the concept of "current value". Generally, only the current value, that is, the latest value, is exposed to the outside world.
  4. So,
RxJS

is suitable for managing state data? The answer is yes!

First of all, because

Observable itself is a push collection of multiple values, the first condition is met!

Secondly, we can implement an

observable that uses the dispatch action pattern to push data to satisfy the second condition!As we all know,

observable

in RxJS can be divided into two types:

cold observable

: producer of pushed values (producer) comes from inside observable.

How many values ​​will be pushed and what kind of values ​​are pushed have been defined when
    observable
  • is created and cannot be changed.
  • producer
  • has a one-to-one relationship with the observer (observer), which is unicast. Whenever
  • observer
  • subscribes, producer will push several predefined values ​​to observer in sequence.
hot observable

: The producer that pushes the value comes from observable externally.

How many values ​​will be pushed, what values ​​will be pushed, and when will be pushed are unknown at creation time.
  • producer
  • and observer have a one-to-many relationship, that is, multicast. Whenever
  • observer
  • subscribes, observer will be registered in the observer list, similar to addListener in other libraries or languages. way of working. When the external
  • producer
  • is triggered or executed, the value will be pushed to all observer at the same time; that is, all observerShared the value pushed by hot observable. The BehaviorSubject provided by

RxJS is a special hot observable, which exposes the interface for pushing data to the outside worldnext function; and there is the concept of "current value", which saves the latest value sent to observer. When a new observer subscribes, it will be immediately retrieved from BehaviorSubjectThat receives the "current value".

Then this shows that it is feasible to use

BehaviorSubject to update the state and save the current value of the state, and the third condition is also met. Simple implementation

Please look at the following code:

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);

The execution code console will print out three values:

Console

 1
 2
 4

The above The code simply implements an example of simple state management:

Initial value of state: 1
  • State value after executing
  • plus
  • : 2State value after executing
  • toDouble
  • : 4
  • The implementation method is quite simple, that is, use
BehaviorSubject

to express the current value of the state: <ul> <li>第一步,通过调用<code>dispatch函数使producer函数执行

  • 第二部,producer函数在内部调用了BehaviorSubjectnext函数,推送了新数据,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越久,越令人受益匪浅。

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

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

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

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

    【相关推荐:react视频教程

    The above is the detailed content of An introduction to using RxJS to manage React application state. For more information, please follow other related articles on the PHP Chinese website!

    Statement:
    This article is reproduced at:segmentfault.com. If there is any infringement, please contact admin@php.cn delete