Reactjs 使用socket.io响应Redux和Websockets
我对React Redux这项技术是新手,我希望您能帮助我实现一些功能 我想用socket(socket.io)实现一个聊天应用程序。首先,用户必须注册(我在服务器端使用passport),然后,如果注册成功,用户必须连接到webSocket 我认为最好的方法是使用一个中间件,比如一个管道来执行所有的操作,并且根据什么样的操作得到中间件,做不同的事情 如果操作类型为Reactjs 使用socket.io响应Redux和Websockets,reactjs,socket.io,redux,middleware,Reactjs,Socket.io,Redux,Middleware,我对React Redux这项技术是新手,我希望您能帮助我实现一些功能 我想用socket(socket.io)实现一个聊天应用程序。首先,用户必须注册(我在服务器端使用passport),然后,如果注册成功,用户必须连接到webSocket 我认为最好的方法是使用一个中间件,比如一个管道来执行所有的操作,并且根据什么样的操作得到中间件,做不同的事情 如果操作类型为AUTH_USER,则创建客户机-服务器连接并设置将来自服务器的所有事件 如果操作类型为MESSAGE则将消息发送到服务器 代码片段
AUTH_USER
,则创建客户机-服务器连接并设置将来自服务器的所有事件
如果操作类型为MESSAGE
则将消息发送到服务器
代码片段:
----socketMiddleware.js---
import { AUTH_USER, MESSAGE } from '../actions/types';
import * as actions from 'actions/socket-actions';
import io from 'socket.io-client';
const socket = null;
export default function ({ dispatch }) {
return next => action => {
if(action.type == AUTH_USER) {
socket = io.connect(`${location.host}`);
socket.on('message', data => {
store.dispatch(actions.addResponse(action.data));
});
}
else if(action.type == MESSAGE && socket) {
socket.emit('user-message', action.data);
return next(action)
} else {
return next(action)
}
}
}
import {createStore, applyMiddleware} from 'redux';
import socketMiddleware from './socketMiddleware';
const createStoreWithMiddleware = applyMiddleware(
socketMiddleware
)(createStore);
const store = createStoreWithMiddleware(reducer);
<Provider store={store}>
<App />
</Provider>
----index.js------
import { AUTH_USER, MESSAGE } from '../actions/types';
import * as actions from 'actions/socket-actions';
import io from 'socket.io-client';
const socket = null;
export default function ({ dispatch }) {
return next => action => {
if(action.type == AUTH_USER) {
socket = io.connect(`${location.host}`);
socket.on('message', data => {
store.dispatch(actions.addResponse(action.data));
});
}
else if(action.type == MESSAGE && socket) {
socket.emit('user-message', action.data);
return next(action)
} else {
return next(action)
}
}
}
import {createStore, applyMiddleware} from 'redux';
import socketMiddleware from './socketMiddleware';
const createStoreWithMiddleware = applyMiddleware(
socketMiddleware
)(createStore);
const store = createStoreWithMiddleware(reducer);
<Provider store={store}>
<App />
</Provider>
从'redux'导入{createStore,applyMiddleware};
从“/socketMiddleware”导入socketMiddleware;
const createStoreWithMiddleware=applyMiddleware(
socketMiddleware
)(createStore);
const store=createStoreWithMiddleware(reducer);
你认为这种做法怎么样?它是一种更好的实现方式吗?扰流板:我目前正在开发一种开源聊天应用程序 通过将操作与中间件分离,甚至将套接字客户端与中间件分离,可以更好地实现这一点。因此,导致如下结果:
- 类型->每个请求的请求、成功、失败类型(非强制性)
- 减速机->用于存储不同的状态
- 操作->发送操作以连接/断开连接/发出/侦听
- 中间件->处理您的操作,并将当前操作传递或不传递给套接字客户端
- 客户端->套接字客户端(socket.io)
export function send(chatId, content) {
const message = { chatId, content };
return {
type: 'socket',
types: [SEND, SEND_SUCCESS, SEND_FAIL],
promise: (socket) => socket.emit('SendMessage', message),
}
}
请注意,socket是一个参数化函数,这样我们就可以在整个应用程序中共享同一个socket实例,而不必担心任何导入问题(稍后我们将演示如何实现)
中间件(socketMiddleware.js):
我们将使用与uses类似的策略,不过对于socket而不是AJAX
我们的套接字中间件将只负责处理套接字请求
中间件将操作传递到套接字客户端,并分派:
- 请求(操作
):正在请求(types[0]
被发送到reducer)action.type
- 成功(操作
):请求成功(类型[1]
,服务器响应为action.type
发送到reducer)action.result
- 失败(操作
):请求失败(将类型[2]
和服务器响应作为操作.type
发送到reducer)操作.error
import io from 'socket.io-client';
// Example conf. You can move this to your config file.
const host = 'http://localhost:3000';
const socketPath = '/api/socket.io';
export default class socketAPI {
socket;
connect() {
this.socket = io.connect(host, { path: socketPath });
return new Promise((resolve, reject) => {
this.socket.on('connect', () => resolve());
this.socket.on('connect_error', (error) => reject(error));
});
}
disconnect() {
return new Promise((resolve) => {
this.socket.disconnect(() => {
this.socket = null;
resolve();
});
});
}
emit(event, data) {
return new Promise((resolve, reject) => {
if (!this.socket) return reject('No socket connection.');
return this.socket.emit(event, data, (response) => {
// Response is the optional callback that you can use with socket.io in every request. See 1 above.
if (response.error) {
console.error(response.error);
return reject(response.error);
}
return resolve();
});
});
}
on(event, fun) {
// No promise is needed here, but we're expecting one in the middleware.
return new Promise((resolve, reject) => {
if (!this.socket) return reject('No socket connection.');
this.socket.on(event, fun);
resolve();
});
}
}
app.js
在应用程序启动时,我们初始化SocketClient
,并将其传递给商店配置
const socketClient = new SocketClient();
const store = configureStore(initialState, socketClient, apiClient);
configureStore.js
我们使用新初始化的SocketClient
将socketMiddleware
添加到存储中间件(还记得我们告诉过您稍后将解释的参数吗?)
[Nothing special]动作类型常量
没什么特别的=你通常会做的事
const SEND = 'redux/message/SEND';
const SEND_SUCCESS = 'redux/message/SEND_SUCCESS';
const SEND_FAIL = 'redux/message/SEND_FAIL';
[无特殊]减速器
export default function reducer(state = {}, action = {}) {
switch(action.type) {
case SEND: {
return {
...state,
isSending: true,
};
}
default: {
return state;
}
}
}
这看起来可能需要做很多工作,但一旦你设置好了,就值得了。您的相关代码将更易于阅读和调试,并且您不太容易出错
PS:您也可以通过AJAX API调用遵循此策略。为此,我使用了来自的函数。它自动生成类型,如挂起
、完成
和拒绝
我在他的回答中使用了与@zurfyx相同的socketService
操作如下所示:
const sendMessage=createAsyncThunk(
“游戏/发送消息”,
异步函数(文本,{getState}){
const roomToken=selectRoomToken(getState());
return wait-socketService.emit('send-message',{text,roomToken});
}
);
减速器看起来像这样:
const gameSlice=createSlice({
名称:'游戏',
初始状态:{},
还原子:{},
外减速器:{
[sendMessage.pending]:(状态、操作)=>{
state.messages.push({
id:action.meta.requestId,
文本:action.meta.arg,
我的朋友:是的,
});
},
[sendMessage.rejected]:(状态、操作)=>{
state.messages=state.messages.filter(
ms=>ms.id!==action.meta.requestId
);
},
},
});
一般来说,这类问题不适合stackoverflow,但您的方法看起来是合理的。唯一让我感到“奇怪”的是有条件地调用next(action)
。通常,您希望调用下一个中间件(dispatcher),例如,让它处理日志记录或您可能在那里拥有的任何其他内容