>  기사  >  웹 프론트엔드  >  RxJS를 사용하여 React 애플리케이션 상태를 관리하는 방법 소개

RxJS를 사용하여 React 애플리케이션 상태를 관리하는 방법 소개

不言
不言앞으로
2019-04-03 10:24:592814검색

이 글은 RxJS를 사용하여 React 애플리케이션 상태를 관리하는 방법을 소개합니다. 필요한 친구들이 참고할 수 있기를 바랍니다.

프런트 엔드 애플리케이션이 점점 더 복잡해짐에 따라 애플리케이션 데이터를 관리하는 방법은 피할 수 없는 문제가 되었습니다. 복잡한 비즈니스 시나리오, 요구 사항의 빈번한 변경, 다양한 애플리케이션 데이터의 상호 의존성이 있는 대규모 프런트 엔드 애플리케이션에 직면할 때 애플리케이션의 상태 데이터를 어떻게 관리하시겠습니까? 우리는 애플리케이션 데이터를 크게 네 가지 범주로 나눌 수 있다고 믿습니다.

이벤트: 즉시 생성되는 데이터는 사용 후 즉시 파기되며 저장되지 않습니다.
  • 비동기: 이벤트와 유사하게 비동기적으로 획득된 데이터이며 즉각적인 데이터이며 저장되지 않습니다.
  • 상태: 시간과 공간에 따라 변하는 데이터는 항상 현재 값/최신 값을 저장합니다.
  • 상수: 고정된 데이터.
  • RxJS는 당연히 비동기 및 이벤트 기반 프로그램을 작성하는 데 적합합니다. 그렇다면 상태 데이터를 어떻게 관리해야 할까요? RxJS를 계속 사용해야 하나요? 적합합니까?

우리는 프런트엔드 커뮤니티의 기존 우수한 상태 관리 솔루션에 대해 조사하고 배웠으며 RxJS를 사용하여 데이터 계층을 설계하는 것에 대해 일부 전문가가 공유한 아이디어와 사례에서 영감을 받았습니다. 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内部。

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

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

  • 将会推送几个值、推送什么样的值以及何时推送在创建时都是未知的。
  • producerobserver是一对多的关系,即是多播的。
  • 每当有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);

// 派遣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

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

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

实现方法挺简单的,就是使用BehaviorSubject

RxJS를 사용하면 Redux, Mobx 등의 기능을 완벽하게 구현하여 상태 데이터를 관리할 수 있습니다. 🎜🎜애플리케이션의 데이터는 상태뿐만 아니라 이벤트, 비동기, 상수 등도 포함합니다. 전체 애플리케이션을 observable로 표현한다면 RxJS의 시퀀스 기반 및 반응형 기능을 활용하여 다양한 유형의 데이터를 스트리밍 방식으로 자유롭게 연결하고 결합할 수 있습니다. , 재사용 가능하고 확장 가능한 비즈니스 모델을 보다 우아하고 효율적으로 추상화할 수 있습니다. 🎜🎜위의 두 가지 이유로 우리는 마침내 RxJS를 기반으로 애플리케이션 상태를 관리하는 솔루션을 설계하기로 결정했습니다. 🎜

원리 소개🎜🎜상태 정의에 있어서 일반적으로 상태는 다음 세 가지 조건을 충족해야 한다고 믿어집니다. 🎜

    🎜은 여러 값을 가진 집합입니다. . 🎜🎜이벤트 또는 액션을 통해 값을 변환하여 새로운 값을 얻을 수 있습니다. 🎜🎜'현재값'이라는 개념이 있습니다. 일반적으로 최신값인 현재값만 외부에 노출됩니다. 🎜
🎜그렇다면 RxJS는 상태 데이터 관리에 적합한가요? 대답은 '예'입니다! 🎜🎜🎜우선 Observable 자체가 여러 값의 푸시 컬렉션이기 때문에 첫 번째 조건이 충족됩니다! 🎜🎜🎜🎜두 번째로, dispatch action 패턴을 사용하여 두 번째 조건을 충족하도록 데이터를 푸시하는 observable을 구현할 수 있습니다! >RxJS의 observable은 두 가지 유형으로 나눌 수 있습니다: 🎜🎜cold observable: 푸시된 값의 생산자(producer) observableInternal에서 옵니다. 🎜🎜🎜푸시할 값의 개수와 푸시할 값의 종류는 observable이 생성될 때 정의되었으며 변경할 수 없습니다. 🎜🎜Producer와 관찰자(observer)는 일대일 관계, 즉 유니캐스트를 갖습니다. 🎜🎜observer 구독이 있을 때마다 producer는 사전 정의된 여러 값을 observer에 순차적으로 푸시합니다. 🎜🎜🎜hot observable: 값을 푸시하는 producerobservable 외부에서 옵니다. 🎜🎜🎜얼마나 많은 값이 푸시되는지, 어떤 값이 푸시되는지, 언제 생성되는지 모두 알 수 없습니다. 🎜🎜Producerobserver는 일대다 관계, 즉 멀티캐스트를 갖습니다. 🎜🎜observer 구독이 있을 때마다 observer는 다른 라이브러리나 언어 작업 방식의 addListener와 유사하게 관찰자 목록에 등록됩니다. . 🎜🎜외부 producer가 트리거되거나 실행되면 값이 모든 observer, 즉 모든 observer에 동시에 푸시됩니다. hot observable에 의해 푸시된 값이 공유됩니다. 🎜🎜🎜RxJS에서 제공하는 BehaviorSubject다음 데이터를 푸시하기 위한 인터페이스를 노출하는 특별한 hot observable입니다. 함수; 그리고 observer로 전송된 최신 값을 저장하는 "현재 값"이라는 개념이 있습니다. 새로운 관찰자가 구독하면 BehaviorSubject에서 즉시 가져옵니다. "현재 값"을 받습니다. 🎜🎜🎜이것은 BehaviorSubject를 사용하여 상태를 업데이트하고 상태의 현재 값을 저장하는 것이 가능하며 세 번째 조건도 충족됨을 보여줍니다. 🎜🎜

간단한 구현🎜🎜다음 코드를 살펴보세요.🎜

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("count", {
  type: "plus"
})
🎜위 코드는 단순히 간단한 예제를 구현합니다. 관리 상태 : 🎜🎜🎜상태 초기값 : 1🎜🎜 plus 실행 후 상태 값 : 2🎜🎜 toDouble 실행 후 상태 값 : 4🎜🎜 🎜구현 메서드는 매우 간단합니다. 즉, BehaviorSubject를 사용하여 상태의 현재 값을 표현합니다. 🎜
  • 第一步,通过调用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视频教程

위 내용은 RxJS를 사용하여 React 애플리케이션 상태를 관리하는 방법 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제