Where and how to add SignalR in redux? - reactjs

I'm creating a react app which implements SignalR and so far I have my connection and all the listeners in the component where I need them. The problem is that I have action creators in Redux which just make a request and get the response in order to call my server and send the data to all the other clients. Once the server emits the event to all clients, one of my listeners gets the data and calls an action creator which just dispatches an action to refresh my redux state.
I feel like I'm not using the action creators in the right way because I have one action creator which just makes the request and gets the response to return it and it's not changing the state.
If a had the socket connection in the store, I would just have to call one action creator and the logic to emit or listen to socket event, would be in other place.
This is my component,
// --- component.js ---
state = {
connection: null,
};
async componentDidMount() {
// handles any network exception and show the error message
try {
await this.setupConnection();
} catch (error) {
this.showNetworkError(`Whoops, there was an error with your network connection. Please reload the page`);
}
setupConnection = () => {
let { connection } = this.state;
this.setState({
connection: (connection = new HubConnectionBuilder().withUrl(HUB_URL).build()),
});
/**
* LISTENERS that are called from the server via websockets
*/
connection.on('InsertTodo', data => {
// action creator
this.props.add(data);
});
connection.on('UpdateTodo', data => {
// action creator
this.props.update(data);
});
}
createTodo = async todo => {
const { connection} = this.state;
// action creator
const createdTodo = await this.props.createTodo(todo);
if (createdTodo) {
// the below sentence calls the server to emit/send the todo item to all other clients
// and the listener in the setupConnection function is executed
connection.invoke('EmitTodoCreate', createdTodo);
} else {
// there was a problem creating the todo
}
};
This is the action creator
// --- actionCreators.js ----
// ------------------------
export const add = todo => {
return async (dispatch) => {
dispatch({
type: ADD_TODO,
payload: todo,
});
};
};
export const createTodo = todo => {
return async (dispatch) => {
dispatch({
type: START_REQUEST,
});
const response = await postTodo(todo);
const result = await response.json();
if (response.ok) {
dispatch({
type: SUCCESS_REQUEST,
});
// returns the todo item created in order to be sent to the server via websockets
return result;
}
dispatch({
type: FAILURE_REQUEST,
error: result.error,
});
return null;
};
};

I think the best solution is to implement a Redux middleware. It is easy, and you can use authentication for establishing connection, and you can dispatch action creators based on different messages emitted from SignalR.
Per the Redux FAQ, the right place for websockets and other similar connections is in Redux middleware.
This is my custom middleware that establishes the connection, and registers the handlers. Please note that I only would like to receive data, and not interested in sending data. I use REST APIs to send data to server.
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;
And inside my store.js file
import signalRMiddleware from '../middlewares/signalRMiddleware';
...
createStore(rootReducer, {}, composeEnhancers(applyMiddleware(signalRMiddleware)));

Related

A non-serializable value was detected when storing mqtt client in reducer redux-toolkit

I am trying to store MQTT client in #redux-toolkit reducer but getting an error 'A non-serializable value was detected
so is there any better approach then storing the client in reducer because I need MQTT client in onCacheEntryAdded to update my cache
const client = MQTT(socketUrl, options)
client.stream.on("error", (err) => {
toast.error(`Connection to ${socketUrl} failed`)
client.end()
return
})
dispatch(updateClient(client))
here is my onCacheEntryAdded function
async onCacheEntryAdded(
arg,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved, getState }
) {
try {
const state = getState()
const client = state.inbox.client
await cacheDataLoaded
client.on("message", (topic, data) => {
const message = JSON.parse(data.toString())
updateCachedData((draft) => {
if (!message.payload) return
draft.messages.unshift(message.payload)
})
})
} catch (err) {}
await cacheEntryRemoved
},
It's a general best practice of Redux that your state should not contain any non-serializable values, just raw data, so storing a client in your state is not advised.
I'm not too familiar with MQTT so I cannot guarantee that this will work. It seems like you could store the socketUrl and options variables in your Redux state. Then construct the MQTT instance inside of the onCacheEntryAdded callback.
async onCacheEntryAdded(
arg,
{ updateCachedData, cacheDataLoaded, cacheEntryRemoved, getState }
) {
try {
const state = getState()
const { socketUrl, options } = state.inbox.clientConfig
const client = MQTT(socketUrl, options)
await cacheDataLoaded
client.on("message", (topic, data) => {
const message = JSON.parse(data.toString())
updateCachedData((draft) => {
if (!message.payload) return
draft.messages.unshift(message.payload)
})
})
} catch (err) {}
await cacheEntryRemoved
client.end()
},
This is similar to the WebSocket streaming update examples in the docs, which call const ws = new WebSocket('ws://localhost:8080') inside of onCacheEntryAdded.

Call redux action within redux-saga inside websocket callback (stomp + sockjs)

I am using redux and redux-saga in my project. Right now using WebSocket I have a problem calling a FETCH_SUCCESS redux action inside a callback of socket response. I tried making the callback a generator as well but didn't work as well.
function* websocketSaga() {
const socket = new SockJS(`${CONFIG.API_URL}/ws`);
const stomp = Stomp.over(socket);
const token = yield select(selectToken);
stomp.connect(
{
Authorization: `Bearer ${token}`,
},
frame => {
stomp.subscribe('/queue/data', message => {
const response = JSON.parse(message.body);
console.log(response); // here is the proper response, it works
put({
type: FETCH_SUCCESS, // here the FETCH_SUCCESS action is not called
payload: response.dataResponse,
});
});
...
....
}
);
}
Or maybe this WebSocket should be implemented in a completely different way in redux-saga?
You won't be able to use yield put inside a callback function. Stompjs knows nothing about sagas, so it doesn't know what it's supposed to do when given a generator function.
The simplest approach, though not necessarily the best, is to go directly to the redux store in the callback, and dispatch the action without involving redux-saga. For example:
import store from 'wherever you setup your store'
// ...
stomp.subscribe('/queue/data', message => {
const response = JSON.parse(message.body);
store.dispatch({
type: FETCH_SUCCESS,
payload: response.dataResponse,
});
});
If you'd like to use a more redux-saga-y approach, I would recommend wrapping the subscription in an event channel. Event channels take a callback-based API and turn it into something that you can interact with using redux-saga's effects such as take
Here's how you might create the event channel:
import { eventChannel } from 'redux-saga';
function createChannel(token) {
return eventChannel(emitter => {
const socket = new SockJS(`${CONFIG.API_URL}/ws`);
const stomp = Stomp.over(socket);
stomp.connect(
{
Authorization: `Bearer ${token}`,
},
frame => {
stomp.subscribe('/queue/data', message => {
const response = JSON.parse(message.body);
emitter(response); // This is the value which will be made available to your saga
});
}
);
// Returning a cleanup function, to be called if the saga completes or is cancelled
return () => stomp.disconnect();
});
}
And then you'd use it like this:
function* websocketSaga() {
const token = yield select(selectToken);
const channel = createChannel(token);
while (true) {
const response = yield take(channel);
yield put({
type: FETCH_SUCCESS,
payload: response.dataResponse,
});
}
}
Promise should be the perfect fit. Just wrap the callback related code in a promise and resolve it in the callback function. After that use the yield to get the data from the promise. I have modified your code with the Promise below.
function* websocketSaga() {
const socket = new SockJS(`${CONFIG.API_URL}/ws`);
const stomp = Stomp.over(socket);
const token = yield select(selectToken);
const p = new Promise((resolve, reject) => {
stomp.connect(
{
Authorization: `Bearer ${token}`,
},
frame => {
stomp.subscribe('/queue/data', message => {
const response = JSON.parse(message.body);
console.log(response); // here is the proper response, it works
resolve(response); // here resolve the promise, or reject if any error
});
...
....
}
);
});
try {
const response = yield p; // here you will get the resolved data
yield put({
type: FETCH_SUCCESS, // here the FETCH_SUCCESS action is not called
payload: response.dataResponse,
});
} catch (ex) {
// handle error here, with rejected value
}
}
I will give you another way of managing this: create a component connected to redux where you will handle the WS subscription. This component will not render anything to the UI but will be useful for handling redux store interactions.
The main idea is, don't put everything into redux-saga, try and split it into multiple parts to make it easier to maintain.
const socket = new SockJS(`${CONFIG.API_URL}/ws`);
function WSConnection(props) {
const {token, fetchDone} = props;
const [stomp, setStomp] = React.useState();
const onMessage = React.useCallback(message => {
const response = JSON.parse(message.body);
fetchDone(response.dataResponse);
}, [fetchDone]);
const onConnect = React.useCallback(frame => {
const subscription = stomp.subscribe('/queue/data', onMessage);
// cleanup subscription
return () => subscription.unsubscribe();
}, [stomp, onMessage]);
const onError = React.useCallback(error => {
// some error happened, handle it here
}, []);
React.useEffect(() => {
const header = {Authorization: `Bearer ${token}`};
stomp.connect(header, onConnect, onError);
// cleanup function
return () => stomp.disconnect();
}, [stomp])
React.useEffect(() => {
setStomp(Stomp.over(socket));
}, []);
return null;
}
const mapStateToProps = state => ({
... // whatever you need from redux store
});
const mapDispatchToProps = dispatch => ({
... // whatever actions you need to dispatch
});
export default connect(mapStateToProps, mapDispatchToProps)(WSConnection);
You can also take it a step further and extract the stomp logic into another file and reuse it wherever you will need it.
It's not wrong to put everything into redux-saga but it's a nice alternative to handle WS connections inside components connected to redux (and easier to understand to people who are not completely familiar with redux-saga and channels etc).
I have the same stack over the years and only recently I faced websockets over Stomp client.
None of the above solutions doesn't work for me both technically and mentally
Reasons:
I don't like channels with Stomp because the only way to manipulate connections in more surgical way you have to use global state object (for me - it's redux). It doesn't seems right even if you storing only random generated IDS (with unsubscribe function it will be... read more here about store serialization
the way with container another pain in the ... (you know where). Again redux and a lot of under-the-hood functionality used without any reason
another way with promises: again without storing helpful connection info and some DI by using promises inside generators. This narrows down the implementation choice
So:
I need to have connection info (I decided to use state but not in: redux, component state. Singleton state). Stomp doesn't force you to place ID but I do because I want to manage connections by myself
I need one entry point without: promises, iterators and a lot of things that will be pain for future-me. One place to "rule them all" (as I want)
- activate: login
- deactivate: logout
- subscribe: componentDidMount
- unsubscribe: componentWillUnmount
DI by request in one place (passing store.dispatch to constructor only if need it) // main topic of the question
And I wrote this implementation that perfectly works for me:
import SockJS from 'sockjs-client';
import {
Client,
IMessage,
messageCallbackType,
StompHeaders,
} from '#stomp/stompjs';
import { Action, Dispatch } from 'redux';
type ConnectionId = string;
interface IServiceConfig {
url: string;
dispatch?: Dispatch;
}
export default class Stomp {
serviceConfig: IServiceConfig = {
dispatch: null,
url: null,
};
ids: ConnectionId[] = [];
stomp: Client;
constructor(config: IServiceConfig) {
this.serviceConfig = { ...config };
this.stomp = new Client();
this.stomp.webSocketFactory = () => {
return (new SockJS(config.url));
};
}
alreadyInQueue = (id: ConnectionId): boolean => {
return Boolean(this.ids.find(_id => id === _id));
};
subscribeByDispatchAction = (
destination: string,
callback: (message: IMessage) => Action,
headers: StompHeaders & {
id: ConnectionId;
},
): void => {
const alreadyInQueue = this.alreadyInQueue(headers.id);
if (!alreadyInQueue) {
this.stomp.subscribe(
destination,
(message) => {
this.serviceConfig.dispatch(callback(message));
},
headers,
);
this.ids.push(headers.id);
return;
}
console.warn(`Already in queue #${headers.id}`);
};
subscribe = (
destination: string,
callback: messageCallbackType,
headers: StompHeaders & {
id: ConnectionId;
},
): void => {
const alreadyInQueue = this.alreadyInQueue(headers.id);
if (!alreadyInQueue) {
this.stomp.subscribe(
destination,
(message) => callback(message),
headers,
);
this.ids.push(headers.id);
this.logState('subscribe');
return;
}
console.warn(`Failed to subscribe over Socks by #${headers.id}`);
};
unsubscribe = (id: ConnectionId, headers?: StompHeaders): void => {
this.stomp.unsubscribe(id, headers);
this.ids.splice(this.ids.indexOf(id), 1);
};
activate = (): void => {
this.stomp.activate();
};
deactivate = (): void => {
if (this.ids.length === 0) {
this.stomp.deactivate();
return;
}
for (let i = 0; i < this.ids.length; i++) {
this.unsubscribe(this.ids[i]);
}
/**
* it seems like it's overkil but
* for me it works only if i do all
* the things as you see below
* - stomp deactivation
* - closing webSockets manually by using native constant // sockjs-client
* - closing webSockets instance by using returned value fron factory
*/
this.stomp.deactivate();
this.stomp.webSocket.close(
this.stomp.webSocket.CLOSED,
);
this.stomp.webSocketFactory().close();
};
getAllIds = (): readonly ConnectionId[] => {
return this.ids;
};
// debug method
logState = (method: string): void => {
/* eslint-disable */
console.group(`Stomp.${method}`);
console.log('this', this);
console.log('this.ids', this.getAllIds());
console.log('this.stomp', this.stomp);
console.groupEnd();
/* eslint-enable */
};
}
My configuration file
import { store } from '~/index';
import Stomp from '~/modules/_Core/services/Stomp';
import appConfig from '~/modules/Common/services/appConfig';
export const StompService = new Stomp({
dispatch: store?.dispatch,
url: `${appConfig.apiV1}/websocket`,
});
I hope that it will help someone

Using a third party SDK with React + Redux

I am working with React + Redux application that uses a third party SDK to connect to a websocket, authenticate with a service, and send and receive data. Here are some examples of what might be done with the SDK:
import SDK from 'third-party';
const client = SDK.init(...);
client.connect();
client.on('auth-challenge', callback => {
// Retrieve auth token from back-end
});
client.on('ready', () => {
client.loadData().then(data => {
// do something with this
});
});
Might it be possible to store this data in my Redux store, or to load the auth token using Sagas and take an action on the SDK once the data is available?
I can imagine that I could import my store into this file and use store.dispatch() to, for example, request a token (via Saga), but how do I know when that token has loaded? Is this something that I simply need to make direct API calls with?
I suggest to put the async part as a promise into the componentDidMount method of one of the connected components and call the dispatcher as the token is received.
import { askForToken } from '../my-helpers/sdk-helper;
class SomeParentComponentsContainer extends Component {
componentDidMount(){
const { dispatch } = this.props;
dispatch({ type: 'GET_TOKEN' })
// async part. Drop it if you use sagas.
askForToken()
.then(token => {
dispatch({ type: 'GET_TOKEN__SUCCESS', payload: { token } })
})
// ----
}
someMethodWhichNeedsTheToken = () => {
// this is available in any connected component now from store
const { sdkToken } = this.props;
....
}
...
}
const mapDispatchToProps = state => ({
sdkToken: state && state.sdkToken
})
export default connect(mapDispatchToProps)(SomeParentComponentsContainer);
Second option is if you use sagas, just keep dispatch({ type: 'GET_TOKEN' }) part in componentDidMount and saga will do the rest.
sagas.js
import { call, put, takeEvery, takeLatest } from 'redux-saga/effects'
import { askForToken } from '../my-helpers/sdk-helper;
function* fetchToken(action) {
try {
const user = yield call(askForToken);
yield put({type: "GET_TOKEN__SUCCESS", token });
} catch (e) {
yield put({type: "GET_TOKEN__FAILS", message: e.message});
}
}
function* mySaga() {
yield takeEvery("GET_TOKEN", fetchToken);
}
See sagas documentation on how to set up the middleware to make saga work.

Issue in React and SignalR where SignalR connection is not identified

Is there any way that we can use react and SignalR together.
I tried out some examples but I was not success.
componentDidMount = () => {
const nick = window.prompt('Your name:', 'John');
//const hubConnection = new HubConnection('http://localhost:5000/chat');
//Said deprecated
const hubConnection = new HubConnectionBuilder().withUrl('http://localhost:5000/chat').build();
this.setState({ hubConnection, nick }, () => {
this.state.hubConnection
.start()
.then(() => console.log('Connection started!'))
.catch(err => console.log('Error while establishing connection :('));
this.state.hubConnection.on('sendToAll', (nick, receivedMessage) => {
const text = `${nick}: ${receivedMessage}`;
const messages = this.state.messages.concat([text]);
this.setState({ messages });
});
});
}
This is an example, where the HubConnection is provided. But when trying to set it to the state hubConnection variable, I'm saying the 'hubConnection variable does not exist on type ReadOnly'
Why I'm receiving this error?
Can someone please help?
Per the Redux FAQ, the right place for websockets and other similar connections is in Redux middleware. Some examples online describe how to add SignalR in a component which is a bad practice. Because if your component gets unmounted then signalR instance has to be recreated.
It is easy to create a middleware, and you can use authentication for establishing connection, and you can dispatch action creators based on different messages emitted from SignalR.
This is my custom middleware that establishes the connection, and registers the handlers. Please note that I only would like to receive data, and not interested in sending data. I use REST APIs to send data to server.
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;
And inside my store.js file
import signalRMiddleware from '../middlewares/signalRMiddleware';
...
createStore(rootReducer, {}, composeEnhancers(applyMiddleware(signalRMiddleware)));

Custom Websocket in Redux Architecture

The more I read about this subject it seems like going down a rabbit hole. This is a new Trading application which receives realtime data through web sockets which is based on a request-response paradigm. There are three separate SPA's in which apart from initial load, every user action triggers a call to the dataStore with a new MDXQuery. So in turn I would need to make fresh subscriptions on a componentDidMount() as well as in the respective ActionCreators.I would like to streamline the code to avoid duplicate code and redundancy.
The below code helps establish a new subscription channel to streams the response through web-socket.(Unlike, most sockets.io code where it comes with a designated open,close,send)
this.subscription = bus.channel(PATH, { mode: bus.wsModes.PULL }).createListener(this.onResponse.bind(this));
this.subscription.subscribe(MDXQuery);
If I read the REDUX documentation as to where should I place the web socket code? It mentions to create a custom middleware.
LINK: https://redux.js.org/faq/codestructure#where-should-websockets-and-other-persistent-connections-live
But I am not very sure how could I go about using this custom web socket code framing my own middleware or doing at the component level would help to mimic this strategy.
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);
}
}
}
Any design inputs would really help!!
I wrote that FAQ entry and example.
If I understand your question, you're asking about how to dynamically create additional subscriptions at runtime?
Since a Redux middleware can see every dispatched action that is passed through the middleware pipeline, you can dispatch actions that are only intended as commands for a middleware to do something. Now, I'm not sure what an MDXQuery is, and it's also not clear what you're wanting to do with the messages received from these subscriptions. For sake of the example, I'll assume that you want to either dispatch Redux actions whenever a subscription message is received, or potentially do some custom logic with them.
You can write a custom middleware that listens for actions like "CREATE_SUBSCRIPTION" and "CLOSE_SUBSCRIPTION", and potentially accepts a callback function to run when a message is received.
Here's what that might look like:
// Add this to the store during setup
const subscriptionMiddleware = (storeAPI) => {
let nextSubscriptionId = 0;
const subscriptions = {};
const bus = createBusSomehow();
return (next) => (action) => {
switch(action.type) {
case "CREATE_SUBSCRIPTION" : {
const {callback} = action;
const subscriptionId = nextSubscriptionId;
nextSubscriptionId++;
const subscription = bus.channel(PATH, { mode: bus.wsModes.PULL })
.createListener((...args) => {
callback(dispatch, getState, ...args);
});
subscriptions[subscriptionId] = subscription;
return subscriptionId;
}
case "CLOSE_SUBSCRIPTION" : {
const {subscriptionId} = action;
const subscription = subscriptions[subscriptionId];
if(subscription) {
subscription.close();
delete subscriptions[subscriptionId];
}
return;
}
}
}
}
// Use over in your components file
function createSubscription(callback) {
return {type : "CREATE_SUBSCRIPTION", callback };
}
function closeSubscription(subscriptionId) {
return {type : "CLOSE_SUBSCRIPTION", subscriptionId};
}
// and in your component:
const actionCreators = {createSubscription, closeSubscription};
class MyComponent extends React.Component {
componentDidMount() {
this.subscriptionId = this.props.createSubscription(this.onMessageReceived);
}
componentWillUnmount() {
this.props.closeSubscription(this.subscriptionId);
}
}
export default connect(null, actionCreators)(MyComponent);
I tried out your solution for my own problem which involves creating a socket instance only when a user is logged and here is how my code looks:
const socketMiddleWare = url => store => {
const socket = new SockJS(url, [], {
sessionId: () => custom_token_id
});
return next => action => {
switch (action.type) {
case types.USER_LOGGED_IN:
{
socket.onopen = e => {
console.log("Connection", e.type);
store.dispatch({
type: types.TOGGLE_SOCK_OPENING
});
if (e.type === "open") {
store.dispatch({
type: types.TOGGLE_SOCK_OPENED
});
createSession(custom_token_id, store);
const data = {
type: "GET_ACTIVE_SESSIONS",
JWT_TOKEN: Cookies.get("agentClientToken")
};
store.dispatch({
type: types.GET_ACTIVE_SESSIONS,
payload: data
});
}
};
socket.onclose = () => {
console.log("Connection closed");
store.dispatch({
type: types.POLL_ACTIVE_SESSIONS_STOP
});
// store.dispatch({ type: TOGGLE_SOCK_OPEN, payload: false });
};
socket.onmessage = e => {
console.log(e)
};
if (
action.type === types.SEND_SOCKET_MESSAGE
) {
socket.send(JSON.stringify(action.payload));
return;
} else if (action.type === types.USER_LOGGED_OUT) {
socket.close();
}
next(action);
}
default:
next(action);
break;
}
};
};
It doesn't work though but could you point me in the right direction. Thanks.

Resources