Redux 是一种用于 React 应用程序的流行状态管理库,它可与 Web sockets 配合使用,以实现客户端与服务器之间的实时通信。redux-ws-middleware 是 Redux 的中间件,可以轻松将 WebSocket 集成到 React 和 Redux 应用程序中。在本文中,我们将深入研究 redux-ws-middleware 软件包,了解如何使用它在 Redux 应用程序中建立实时通信。
当应用程序使用 API(WebSockets)进行实时通信时,该库提供了许多有用的工具。
安装
# using npm
npm install redux-ws-middleware
# using yarn
yarn add redux-ws-middleware
一旦安装了该库,就可以将其添加为 Redux 中间件,在应用程序中使用。您只需使用该库创建一个中间件实例,并将您的配置传递给它即可。
示例
// File: "src/store/Socket/socketOptions.ts"
import { Dispatch } from '@reduxjs/toolkit';
import { MiddlewareOptions } from 'redux-ws-middleware';
import { socketMessageHandler } from './socketMessageHandler';
// What client sends to the server (RAW).
type ScoketReq = {
method: string;
data: Record<string, unknown>;
};
// What server sends to the client (RAW).
type SocketRes = {
[method: string]: Record<string, unknown>;
};
// What server expects to receive.
type ScoketSerializedReq = {
[method: string]: Record<string, unknown>;
};
// What client expects to receive.
type SocketDeserializedRes = Record<string, unknown>;
export const socketOptions: MiddlewareOptions<ScoketReq, SocketRes, ScoketSerializedReq, SocketDeserializedRes> = {
url: 'ws://localhost:3000',
// The types that need to be dispatched with the redux dispatch
// to control the lib.
//
// [0] ('/Request$/' in our case) - sends signal to the API.
// The lib listens to the actions that end with "Request" and
// sends action payload to the server.
// [1] ('CONNECT' in our case) - opens the socket connection.
// [2] ('DISCONNECT' in our case) - closes the socket connection.
//
// WARNING: The order matters!
actionTypes: [/Request$/, 'CONNECT', 'DISCONNECT'],
// The action types that lib dispatches. These types can be
// handled in reducers or slices.
//
// [0] ('CONNECTED' in our case) - received when the socket
// connection has been establised.
// [1] ('DISCONNECTED' in our case) - received when the socket
// connection has been closed (with additional details in the
// action payload).
//
// WARNING: The order matters!
completedActionTypes: ['CONNECTED', 'DISCONNECTED'],
// The lib calls the function when receives the message.
//
// "res" is already deserialized response (SocketDeserializedRes).
// See below how to deserialize the response to client-friendly
// format (if needed).
// "dispatch" is a redux dispatch we can call actions with.
onMessage: (res, dispatch) => {
// Here you can identify your responses by methods and
// take them to the reducers/slices by using dispatch.
return socketMessageHandler(res, dispatch);
},
// To avoid infinite loop and spamming the lib tries to
// reconnect back to the server when the connection gets
// unnexpecnely closed.
//
// In our case first try will be immedialely after the
// disconnection. If no success then the lib will try in one
// seccond. If no success - five seconds. And then every next try
// will be in five secconds after faulure.
//
// NOTE: The array size is not limited.
reconnectionInterval: [0, 1000, 5000],
// This should be used for the API request serialization.
// For example: converting payloads from camelCase to snake_case.
// serialize: (req: ScoketReq) => ScoketSerializedReq
serialize: ({ method, data }) => ({ [method]: data }),
// This should be used for the API response deserialization.
// For example: converting API responses from snake_case to camelCase.
// deserialize: (res: SocketRes) => SocketDeserializedRes
deserialize: (res) => res[Object.keys(res)[0]],
// NOTE: The lib also takes many other parameters to make
// everything customized.
// For more info visit the git repository with the examples.
//
// ...
};
现在,让我们将中间件连接到 Redux。下面是一个如何使用 Redux 工具包的示例。
示例:
// File: "src/store/appStore.ts"
import { configureStore } from '@reduxjs/toolkit';
import { createSocketMiddleware } from 'redux-ws-middleware';
import { socketOptions } from './Socket/socketOptions';
import { appReducer } from './appReducer';
export const appStore = configureStore({
reducer: appReducer,
middleware: (getDefaultMiddleware) => {
const middleware = getDefaultMiddleware({ serializableCheck: false });
// Adding our middleware to the redux middleware
return [...middleware, createSocketMiddleware(socketOptions)];
}
});
Bonus
我设计了一种方法来处理通信,并将响应发送给特定的 reducer/slice。
示例:
// File: "src/store/Socket/socketMessageHandler.ts"
import { Dispatch } from '@reduxjs/toolkit';
import { sessionActions } from '../Session/sessionSlice';
import { todosActions } from '../Todos/todosSlice';
// I found it really easy to map method name
// to the [success, failure] actions.
const socketHandlers = {
get_session: [
sessionActions.getSessionFulfilled,
sessionActions.getSessionRejected
],
create_session: [
sessionActions.createSessionFulfilled,
sessionActions.createSessionRejected
],
delete_session: [
sessionActions.deleteSessionFulfilled,
sessionActions.deleteSessionRejected
],
get_todos: [
todosActions.getTodosFullfiled,
todosActions.getTodosRejected
],
create_todo: [
todosActions.createTodoFullfiled,
todosActions.createTodoRejected
],
update_todo: [
todosActions.updateTodoFullfiled,
todosActions.updateTodoRejected
]
};
export const socketMessageHandler = (res, dispatch: Dispatch) => {
const { method, result, error } = res;
if (!socketHandlers[method]) return;
const [fulfilled, failure] = socketHandlers[method];
// We need to figure out how can we recognize success
// and failure responses.
// My suggestion is having error field which can be
// message (string) or code (number)
// The code can be mapped to some mesage on the client side.
// In my case, I receive the "error" from the API.
if (error && failure) {
dispatch(failure(error));
} else if (!error && fulfilled) {
dispatch(fulfilled(result));
}
};
一切准备就绪后,我们可以看看如何向服务器发送信号。要向服务器发送信号,我们需要编写 redux 操作。
示例:
import { createAction } from '@reduxjs/toolkit';
import type { AppThunkAction } from '../appStoreTypes';
// This action is needed just to send the signal to the server.
// The action's type ends with "Request" so the lib will
// recognize it and send to the server once action gets dispatched.
//
// WARNING: This is private action and shouldn't be dispatched from any
// other place except the other actions
export const createSessionRequest = createAction<SubscribeApiReq>('session/createSessionRquest');
// This action should be dispatched from a react component.
export const createSession = (payload: any): AppThunkAction => {
return (dispatch) => {
dispatch(createSessionRequest({ method: 'create_sesssion', payload }));
};
};
详细例子:
GitHub:https://github.com/maxzinchenko/redux-ws-middleware/tree/master/examples
版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。