溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

使用RxJS管理React應用狀態(tài)的方法

發(fā)布時間:2020-10-19 17:33:50 來源:億速云 閱讀:362 作者:小新 欄目:web開發(fā)

這篇文章主要介紹了使用RxJS管理React應用狀態(tài)的方法,具有一定借鑒價值,需要的朋友可以參考下。希望大家閱讀完這篇文章后大有收獲。下面讓小編帶著大家一起了解一下。

隨著前端應用的復雜度越來越高,如何管理應用的數據已經是一個不可回避的問題。當你面對的是業(yè)務場景復雜、需求變動頻繁、各種應用數據互相關聯(lián)依賴的大型前端應用時,你會如何去管理應用的狀態(tài)數據呢?

我們認為應用的數據大體上可以分為四類:

  • 事件:瞬間產生的數據,數據被消費后立即銷毀,不存儲。
  • 異步:異步獲取的數據;類似于事件,是瞬間數據,不存儲。
  • 狀態(tài):隨著時間空間變化的數據,始終會存儲一個當前值/最新值。
  • 常量:固定不變的數據。

RxJS天生就適合編寫異步和基于事件的程序,那么狀態(tài)數據用什么去管理呢?還是用RxJS嗎? 合不合適呢?

我們去調研和學習了前端社區(qū)已有的優(yōu)秀的狀態(tài)管理解決方案,也從一些大牛分享的關于用RxJS設計數據層的構想和實踐中得到了啟發(fā):

  1. 使用RxJS完全可以實現(xiàn)諸如Redux,Mobx等管理狀態(tài)數據的功能。
  2. 應用的數據不是只有狀態(tài)的,還有事件、異步、常量等等。如果整個應用都由observable來表達,則可以借助RxJS基于序列且可響應的的特性,以流的方式自由地拼接和組合各種類型的數據,能夠更優(yōu)雅更高效地抽象出可復用可擴展的業(yè)務模型。

出于以上兩點原因,最終決定基于RxJS來設計一套管理應用的狀態(tài)的解決方案。

原理介紹

對于狀態(tài)的定義,通常認為狀態(tài)需要滿足以下3個條件:

  1. 是一個具有多個值的集合。
  2. 能夠通過event或者action對值進行轉換,從而得到新的值。
  3. 有“當前值”的概念,對外一般只暴露當前值,即最新值。

那么,RxJS適合用來管理狀態(tài)數據嗎?答案是肯定的!

首先,因為Observable本身就是多個值的推送集合,所以第一個條件是滿足的!

其次,我們可以實現(xiàn)一個使用dispatch action模式來推送數據的observable來滿足第二個條件!

眾所周知,RxJS中的observable可以分為兩種類型:

cold observable: 推送值的生產者(producer)來自observable內部。

  • 將會推送幾個值以及推送什么樣的值已在observable創(chuàng)建時被定義下來,不可改變。
  • producer與觀察者(observer) 是一對一的關系,即是單播的。
  • 每當有observer訂閱時,producer都會把預先定義好的若干個值依次推送給observer。

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

  • 將會推送幾個值、推送什么樣的值以及何時推送在創(chuàng)建時都是未知的。
  • producerobserver是一對多的關系,即是多播的。
  • 每當有observer訂閱時,會將observer注冊到觀察者列表中,類似于其他庫或語言中的addListener的工作方式。
  • 當外部的producer被觸發(fā)或執(zhí)行時,會將值同時推送給所有的observer;也就是說,所有的observer共享了hot observable推送的值。

RxJS提供的BehaviorSubject就是一種特殊的hot observable,它向外暴露了推送數據的接口next函數;并且有“當前值”的概念,它保存了發(fā)送給observer的最新值,當有新的觀察者訂閱時,會立即從BehaviorSubject那接收到“當前值”。

那么這說明使用BehaviorSubject來更新狀態(tài)并保存狀態(tài)的當前值是可行的,第三個條件也滿足了。

簡單實現(xiàn)

請看以下的代碼:

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;  // 狀態(tài)的初始值
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);

執(zhí)行代碼控制臺會打印出三個值:

Console

 1
 2
 4

上面的代碼簡單實現(xiàn)了一個簡單管理狀態(tài)的例子:

  • 狀態(tài)的初始值: 1
  • 執(zhí)行plus之后的狀態(tài)值: 2
  • 執(zhí)行toDouble之后的狀態(tài)值: 4

實現(xiàn)方法挺簡單的,就是使用BehaviorSubject來表達狀態(tài)的當前值:

  • 第一步,通過調用dispatch函數使producer函數執(zhí)行
  • 第二部,producer函數在內部調用了BehaviorSubjectnext函數,推送了新數據,BehaviorSubject的當前值更新了,也就是狀態(tài)更新了。

不過寫起來略微繁瑣,我們對其進行了封裝,優(yōu)化后寫法見下文。

使用操作符來創(chuàng)建狀態(tài)數據

我們自定義了一個操作符state用來創(chuàng)建一個能夠通過dispatch action模式推送新數據的BehaviorSubject,我們稱她為stateObservable

const count$ = state({
  // 狀態(tài)的唯一標識名稱
  name: "count",
    
  // 狀態(tài)的默認值
  defaultValue: 1,
    
  // 數據推送的生產者函數
  producer(next, value, action) {
    switch (action.type) {
      case "plus":
        next(value + 1);
        break;
      case "toDouble":
        next(value * 2);
        break;
    }
  }
});

更新狀態(tài)

在你想要的任意位置使用函數dispatch派遣action即可更新狀態(tài)!

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

異步數據

RxJS的一大優(yōu)勢就在于能夠統(tǒng)一同步和異步,使用observable處理數據你不需要關注同步還是異步。

下面的例子我們使用操作符frompromise轉換為observable

指定observable作為狀態(tài)的初始值(首次推送數據)
const todos$ = state({
  name: "todos",
    
  // `observable`推送的數據將作為狀態(tài)的初始值
  initial: from(getAsyncData())
    
  //...
  
});
producer推送observable
const todos$ = state({
  name: "todos",
    
  defaultValue: []
    
  // 數據推送的生產者函數
  producer(next, value, action) {
    switch (action.type) {
      case "getAsyncData":
        next(
          from(getAsyncData())
        );
        break;
    }
  }
});

執(zhí)行getAsyncData之后,from(getAsyncData())的推送數據將成為狀態(tài)的最新值。

衍生狀態(tài)

由于狀態(tài)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
      });
    });
  }
}

我們可以再優(yōu)化下,利用高階組件封裝一個裝飾器函數@subscription,顧名思義,就是為React組件訂閱observable以響應推送數據的變化;它會將observable推送的數據轉換為React組件的props。

@subscription({
  todos: todos$
})
class TodoList extends React.Component {
  render() {
    return (
      <p className="todolist">
        <h2 className="header">任務列表</h2>
        {this.props.todos.map((item, n) => {
          return <TodoItem item={item} key={item.desc} />;
        })}
      </p>
    );
  }
}

總結

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

  • 因為它基于observable序列提供了較高層次的抽象,并且是觀察者模式,可以盡可能地減少各組件各模塊之間的耦合度,大大減輕了定位BUG和重構的負擔。
  • 因為是基于observable序列來編寫代碼的,所以遇到復雜的業(yè)務場景,總能按照一定的順序使用observable描述出來,代碼的可讀性很強。并且當需求變動時,我可能只需要調整下observable的順序,或者加個操作符就行了。再也不必因為一個復雜的業(yè)務流程改動了,需要去改好幾個地方的代碼(而且還容易改出BUG,笑~)。

所以,以上基于RxJS的狀態(tài)管理方案,對我們來說是一個必需品,因為我們項目中大量使用了RxJS,如果狀態(tài)數據也是observable,對我們抽象可復用可擴展的業(yè)務模型是一個非常大的助力。當然了,如果你的項目中沒有使用RxJS,也許ReduxMobx是更合適的選擇。

這套基于RxJS的狀態(tài)管理方案,我們已經用于開發(fā)公司的商用項目,反饋還不錯。所以我們決定把這套方案整理成一個js lib,取名為:Floway,并在github上開源:

  • github源碼:https://github.com/shayeLee/floway
  • 使用文檔:https://shayelee.github.io/floway

感謝你能夠認真閱讀完這篇文章,希望小編分享使用RxJS管理React應用狀態(tài)的方法內容對大家有幫助,同時也希望大家多多支持億速云,關注億速云行業(yè)資訊頻道,遇到問題就找億速云,詳細的解決方法等著你來學習!

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI