小程式中 Redux 的使用
作者 | 張鵬
在我們的一款小程式中聊天部分主要是基於 Redux 來維護資料部分的。為什麼使用了 Redux ?這也是符合了使用 Redux 的一些原則的。那麼哪些情況使用 Redux 比較好呢?
使用者的使用方式複雜
不同身份的使用者有不同的使用方式(比如普通使用者和管理員)
多個使用者之間可以協作
與伺服器大量互動,或者使用了 Socket/">WebSocket
View 要從多個來源獲取資料
我們的聊天功能基於 WebSocket 互動資料,使用方式較為複雜,多個地方都會影響聊天呈現的資料內容。並且與伺服器互動量比較大,UI 上呈現的內容受到多個地方的影響。如下圖:
圖中展示了 Redux 的三大塊業務實現部分與業務部分的互動邏輯,其中資料會反應在首頁和聊天介面,而首頁及聊天介面的一些操作又會通過 Action 反饋到 Redux 的資料物件上。另外 Websocket 和 Http 網路部分也會有很多資料反饋到 Redux 的資料物件上。
Redux 設計思想
簡單總結為兩句話:
(1)Web 應用是一個狀態機,檢視與狀態是一一對應的。
(2)所有的狀態,儲存在一個物件裡面。
Redux 的三大原則
-
單一資料來源
-
State 是隻讀的
-
使用純函式來執行修改
其工作邏輯如下圖所示:
Store
Store 就是儲存資料的地方,你可以把它看成一個容器。整個應用只能有一個 Store。
import { createStore } from 'redux'; const store = createStore(fn);
通過 Store 可以獲取到 State 物件,State 為時點的資料集合,即 Store 的一個快照。
import { createStore } from 'redux'; const store = createStore(fn); const state = store.getState();
Action
State 的變化,會導致 View 的變化。但是,使用者接觸不到 State,只能接觸到 View。所以,State 的變化必須是 View 導致的。Action 就是 View 發出的通知,表示 State 應該要發生變化了。
const action = { type: 'ADD_TODO',payload: 'Learn Redux' };
Dispatch
store.dispatch()
是 View 發出 Action 的唯一方法。
import { createStore } from 'redux';const store = createStore ( fn ); store . dispatch ({ type : 'ADD_TODO' , payload :
'Learn Redux'
});Subscribe
Store 允許使用 store.subscribe
方法設定監聽函式,一旦 State 發生變化,就自動執行這個函式。
import { createStore } from 'redux'; const store = createStore(reducer); store.subscribe(listener);
小程式
在 Redux 的使用中我們主要會去實現兩個部分,一是 Action 部分,去構造定義要傳送的 Action 的資料格式等,另一部分是 Reducer 部分,即 Dispatch 分發的 Action 的具體相應處理部分。Reducer 即接收原 State 和 Action,根據當前 Action 重新建立一份新的 State,然後返回這個 State。
訊息的處理邏輯一開始並不是很好,傳送訊息、接收訊息、傳送中、接收中等各種訊息的狀態,都會單獨傳送不同的 Action 這也導致 Reducer 的維護變得非常困難,而且導致很多不一致的地方。
後來改為一個 Action 做統一處理,處理起來簡單了很多,將原有的多種 Action,多種接收 Action 並處理的邏輯統一成一種,當然如果是維護的不同資料那麼還是需要分開來處理的。
修改後 Action 的實現
修改後訊息 Action 介面如下:
onMessage(message, doctorId)
如兩處修改訊息 Action 的使用:
-
接收訊息,WebSocket 接收到新的訊息時,因為接收到的訊息分為傳送出去的,和接收到的兩種:
onMessage({ ...message, sending: { status: 0 }}, message.from === config.patientId ? message.to : message.from );
-
傳送訊息,本地傳送訊息時,將其更新到介面上,並呼叫WebSocket傳送介面將其傳送出去。
const sendText = async (doctorId, text) => { const message = { typ: MSG.TEXT, content: toRealText(text), to: doctorId, mine: true, sending: { status: 1 }, guid: guid(), from: config.patientId, created: Date.now() } onMessage(message, doctorId) }
修改後 Reducer 的實現
在 Reducer 統一提供一處訊息 Action 的處理方式,避免之前的多處處理導致的資料不一致的情況:
[CHAT_MESSAGE] (_state, _action) { // 拷貝一份 state let state = {..._state}; // 提取訊息引數 _handle(_state, _action); // 處理訊息物件 // 修改 state // ... return state; }
修改後 UI 訂閱狀態
最後我們需要將 State 中維護的資料物件顯示到 UI 上,在 Javascript 中我們可以使用 @connect 在頁面上加上修飾,通過 @connect 實現內容的 subscribe
過程,將 messages 方法注入到頁面中的 data 中。
@connect({ messages(state) { const chat = state.chat[this.doctorId]; if (chat) { return chat.messages; } return []; } })
對於首頁也是同樣的道理,在首頁不需要訊息列表,但是需要小時列表的摘要資訊以顯示有多少種訊息列表。也即當前維護著的對話數量。
@connect({ sessions(state) { let sessions = [] for (let id in state.chat) { let session = state.chat[id] sessions.push(session.session) } sessions.sort((a, b) => { return b.updated - a.updated }) return sessions } });
總結
小程式裡使用到的內容較為簡單,Redux 原本也就是簡化 Web 中狀態和介面簡單對應關係,使用時只需要關注其三大原則即可。並儘可能地統一相同的修改操作,保持資料的統一性。
參考
http://www.redux.org.cn/ http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html
全文完
以下文章您可能也會感興趣:
我們正在招聘 Java 工程師,歡迎有興趣的同學投遞簡歷到 [email protected] 。
杏仁技術站
長按左側二維碼關注我們,這裡有一群熱血青年期待著與您相會。