我正在设计一个React网站,使用Redux作为状态商店,主要是向用户显示当前的项目数量,使用实时更新来使用SignalR更新项目数量。
我想这样做的方法是让SignalR发送项目更新消息,以便在连接到服务器中心时初始化起始填充,以及随着时间的推移通过相同的消息类型进行更新。我将有一个函数接收SignalR消息并将其转换为Redux操作并调度到Redux存储,然后Redux存储将使用该操作来更新状态,然后是UI。
所以这个想法是
1)连接到SignalR服务器中心,设置客户端处理程序功能 对于ItemUpdate消息
2)当服务器收到来自的Connect()时 客户端,它为该中的所有当前项发送ItemUpdate消息 人口
3)客户端从SignalR接收这些消息, 转换为动作并分派到Redux商店
4)Redux 根据新项目信息和UI更新商店 显示它
5)服务器实现已添加或更新的项目 发送新的ItemUpdate消息以进行更新到客户端
6)重复
但是我不确定我应该保留hub单例的位置,因为这似乎与React / Redux设计相反。有人可以建议最好的方法吗?
我的主要应用
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import './index.css';
import registerServiceWorker from './registerServiceWorker';
import 'rxjs';
import store from './store/index';
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root') as HTMLElement
);
registerServiceWorker();
我的商店创建文件
import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../reducers/index';
import signalRMiddleware from '../signalr/middleware';
const store = createStore(rootReducer, applyMiddleware(signalRMiddleware));
export default store;
我的服务器出站SignalR消息的中间件(由于我无法访问我需要的集线器对象而被注释掉
export default function signalRMiddleware(store: any) {
return (next: any) => (action: any) => {
if (action.signalR) {
switch (action.type) {
default:
{
//const myCurrentState = store.getState().objectWithinState;
//_hub.server.methodOnTheServer2(action.type, myCurrentState);
}
}
}
return next(action);
}
}
现在对于传入的消息......这是我从一个在线示例获得的signalR启动函数的shell - 尚未实现,因为我还没有集线器和连接但不确定应该去哪里
export function signalRStart(store: any, callback: Function) {
_hub = $.connection.myHubName;
_hub.client.firstClientFunction = (p1: any) => {
store.dispatch({ type: "SERVER_CALLED_ME", a: p1 });
}
_hub.client.secondClientFunction = (p1: string, p2: string) => {
store.dispatch({ type: "SERVER_CALLED_ME_2", value: p1 + p2 });
}
}
$.connection.hub.start(() => callback());
}
这是我在网站上给出的示例,我发现代码将它们组合在一起,但是我没有看到它如何与我的主索引页面中的React / Redux集成,我必须通过创建的商店到提供者组件,所以我不能把集线器创建放在这个下面,因为你需要信号器中间件组件的集线器,它被传递到商店创建
let _hub;
let store = createStore(
todoApp,
// applyMiddleware() tells createStore() how to handle middleware
applyMiddleware(signalRMiddleware)
)
// Make sure signalr is connected
signalRStart(store, () => {
render((...),
document.getElementById("app-container"));
});
有人可以建议将SignalR集成到我的React / Redux应用程序中的最佳方法吗?
答案 0 :(得分:6)
面向将来可能会发现此话题的人。
这是我的自定义中间件,它仅建立连接并注册处理程序。请注意,我只希望接收数据,而对发送数据不感兴趣。
import {
JsonHubProtocol,
HttpTransportType,
HubConnectionBuilder,
LogLevel
} from '@aspnet/signalr'; // version 1.0.4
// action for user authentication and receiving the access_token
import { USER_SIGNED_IN } from '../actions/auth';
const onNotifReceived = res => {
console.log('****** NOTIFICATION ******', res);
};
const startSignalRConnection = connection => connection.start()
.then(() => console.info('SignalR Connected'))
.catch(err => console.error('SignalR Connection Error: ', err));
const signalRMiddleware = ({ getState }) => next => async (action) => {
// register signalR after the user logged in
if (action.type === USER_SIGNED_IN) {
const urlRoot = (window.appConfig || {}).URL_ROOT;
const connectionHub = `${urlRoot}/api/service/hub`;
const protocol = new JsonHubProtocol();
// let transport to fall back to to LongPolling if it needs to
const transport = HttpTransportType.WebSockets | HttpTransportType.LongPolling;
const options = {
transport,
logMessageContent: true,
logger: LogLevel.Trace,
accessTokenFactory: () => action.user.access_token
};
// create the connection instance
const connection = new HubConnectionBuilder()
.withUrl(connectionHub, options)
.withHubProtocol(protocol)
.build();
// event handlers, you can use these to dispatch actions to update your Redux store
connection.on('OperationProgress', onNotifReceived);
connection.on('UploadProgress', onNotifReceived);
connection.on('DownloadProgress', onNotifReceived);
// re-establish the connection if connection dropped
connection.onclose(() => setTimeout(startSignalRConnection(connection), 5000));
startSignalRConnection(connection);
}
return next(action);
};
export default signalRMiddleware;
在我的store.js文件中
import signalRMiddleware from '../middlewares/signalRMiddleware';
...
createStore(rootReducer, {}, composeEnhancers(applyMiddleware(signalRMiddleware)));
答案 1 :(得分:0)
根据Redux常见问题解答,the right place for websockets and other similar connections is in Redux middleware。
Here is a list of existing websocket middle-wares。您可以查看其中的一些源代码,并且很容易了解如何实现自己的自定义中间件:
中间件可以调度动作。这是一个套接字中间件的外观示例,并调度一个监听的动作:
const createMySocketMiddleware = (url) => {
return storeAPI => {
let socket = createMyWebsocket(url);
socket.on("message", (message) => {
storeAPI.dispatch({
type : "SOCKET_MESSAGE_RECEIVED",
payload : message
});
});
return next => action => {
if(action.type == "SEND_WEBSOCKET_MESSAGE") {
socket.send(action.payload);
return;
}
return next(action);
}
}
}
您需要将此中间件应用于您的redux商店
let store = createStore(
some_reducer,
applyMiddleware(createMySocketMiddleware)
)
稍后,在您的应用中。这是动作创建者
const sendSocketMessage = message => ({
type : "SEND_WEBSOCKET_MESSAGE",
payload : message
}
在组件中添加按钮以通过websocket调度操作
class MyComponent extends React.Component {
handleClick = () => {
this.props.sendSocketMessage("This goes to the server");
}
}
export default connect(null, {sendSocketMessage})(MyComponent)