이 글은 RxJS를 사용하여 React 애플리케이션 상태를 관리하는 방법을 소개합니다. 필요한 친구들이 참고할 수 있기를 바랍니다.
프런트 엔드 애플리케이션이 점점 더 복잡해짐에 따라 애플리케이션 데이터를 관리하는 방법은 피할 수 없는 문제가 되었습니다. 복잡한 비즈니스 시나리오, 요구 사항의 빈번한 변경, 다양한 애플리케이션 데이터의 상호 의존성이 있는 대규모 프런트 엔드 애플리케이션에 직면할 때 애플리케이션의 상태 데이터를 어떻게 관리하시겠습니까? 우리는 애플리케이션 데이터를 크게 네 가지 범주로 나눌 수 있다고 믿습니다.
이벤트: 즉시 생성되는 데이터는 사용 후 즉시 파기되며 저장되지 않습니다.RxJS
는 당연히 비동기 및 이벤트 기반 프로그램을 작성하는 데 적합합니다. 그렇다면 상태 데이터를 어떻게 관리해야 할까요? RxJS
를 계속 사용해야 하나요? 적합합니까? 우리는 프런트엔드 커뮤니티의 기존 우수한 상태 관리 솔루션에 대해 조사하고 배웠으며 RxJS
를 사용하여 데이터 계층을 설계하는 것에 대해 일부 전문가가 공유한 아이디어와 사례에서 영감을 받았습니다. 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); // 派遣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
上面的代码简单实现了一个简单管理状态的例子:
plus
之后的状态值: 2toDouble
之后的状态值: 4实现方法挺简单的,就是使用BehaviorSubject
RxJS
를 사용하면 Redux
, Mobx
등의 기능을 완벽하게 구현하여 상태 데이터를 관리할 수 있습니다. 🎜🎜애플리케이션의 데이터는 상태뿐만 아니라 이벤트, 비동기, 상수 등도 포함합니다. 전체 애플리케이션을 observable
로 표현한다면 RxJS
의 시퀀스 기반 및 반응형 기능을 활용하여 다양한 유형의 데이터를 스트리밍 방식으로 자유롭게 연결하고 결합할 수 있습니다. , 재사용 가능하고 확장 가능한 비즈니스 모델을 보다 우아하고 효율적으로 추상화할 수 있습니다. 🎜🎜위의 두 가지 이유로 우리는 마침내 RxJS
를 기반으로 애플리케이션 상태를 관리하는 솔루션을 설계하기로 결정했습니다. 🎜원리 소개🎜🎜상태 정의에 있어서 일반적으로 상태는 다음 세 가지 조건을 충족해야 한다고 믿어집니다. 🎜
이벤트
또는 액션
을 통해 값을 변환하여 새로운 값을 얻을 수 있습니다. 🎜🎜'현재값'이라는 개념이 있습니다. 일반적으로 최신값인 현재값만 외부에 노출됩니다. 🎜RxJS
는 상태 데이터 관리에 적합한가요? 대답은 '예'입니다! 🎜🎜🎜우선 Observable
자체가 여러 값의 푸시 컬렉션이기 때문에 첫 번째 조건이 충족됩니다! 🎜🎜🎜🎜두 번째로, dispatch action
패턴을 사용하여 두 번째 조건을 충족하도록 데이터를 푸시하는 observable
을 구현할 수 있습니다! >RxJS의 observable
은 두 가지 유형으로 나눌 수 있습니다: 🎜🎜cold observable
: 푸시된 값의 생산자(producer
) observable
Internal에서 옵니다. 🎜🎜🎜푸시할 값의 개수와 푸시할 값의 종류는 observable
이 생성될 때 정의되었으며 변경할 수 없습니다. 🎜🎜Producer
와 관찰자(observer
)는 일대일 관계, 즉 유니캐스트를 갖습니다. 🎜🎜observer
구독이 있을 때마다 producer
는 사전 정의된 여러 값을 observer
에 순차적으로 푸시합니다. 🎜🎜🎜hot observable
: 값을 푸시하는 producer
는 observable
외부에서 옵니다. 🎜🎜🎜얼마나 많은 값이 푸시되는지, 어떤 값이 푸시되는지, 언제 생성되는지 모두 알 수 없습니다. 🎜🎜Producer
와 observer
는 일대다 관계, 즉 멀티캐스트를 갖습니다. 🎜🎜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" })
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; }) );
我们可能会在组件的生命周期内订阅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
,也许Redux
和Mobx
是更合适的选择。
这套基于RxJS
的状态管理方案,我们已经用于开发公司的商用项目,反馈还不错。所以我们决定把这套方案整理成一个js lib
,取名为:Floway
,并在github
上开源:
【相关推荐:react视频教程】
위 내용은 RxJS를 사용하여 React 애플리케이션 상태를 관리하는 방법 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!