作者 | 张鹏
Android 程序员,关注大前端各种新兴技术。
在我们的一款小程序中聊天部分主要是基于 Redux 来维护数据部分的。为什么使用了 Redux ?这也是符合了使用 Redux 的一些原则的。那么哪些情况使用 Redux 比较好呢?
用户的使用方式复杂
不同身份的用户有不同的使用方式(比如普通用户和管理员)
多个用户之间可以协作
与服务器大量交互,或者使用了 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';
});
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 工程师,欢迎有兴趣的同学投递简历到 rd-hr@xingren.com 。
杏仁技术站
长按左侧二维码关注我们,这里有一群热血青年期待着与您相会。