I have just had to implement this and found it hard to come across a simple working solution out there.

So here is mine:

Prerequisites

Let’s consider the following WebSocket object:

1
const mySocket = new WebSocket("ws://www.xyz.com/socketServer", "protocol");

We can listen to incoming messages with

1
2
3
mySocket.onmessage = (message) => {
console.log(message.data);
}

and close the websocket with

1
mySocket.close();

Setup redux-saga

To listen recurring events, saga provides an eventChannel object.

Basic usage

1
2
3
4
5
6
7
8
// 1- Create a channel that listens to messages from the server
const channel = yield call(createEventChannel);
while (true) {
// 2- wait for the next message from the server
const {message} = yield take( channel);
// 3- Send the message payload to the reducer
yield put({type: WEBSOCKET_MESSAGE_RECEIVED, message});
}

Once the channel is created (we will get there in a second), we can use yield take(channel) to listen to the message received. By putting that call in a while(true) {...} loop we can be listening to messages forever.

Create the Event Channel

The eventChannel takes a callback parameter with an emit argument to put messages onto the eventChannel. This callback function should return another function to be executed when the connection is closed.

1
2
3
4
5
6
7
8
9
10
11
function createEventChannel (socket) {
return eventChannel(emit => {
// call emit when a message is received
mySocket.onmessage((message) => emit(message.data));
// Return a function to be called when done listening
return () => {
mySocket.close();
};
});
}

Plug in that function in the example above and you are ready to go.

Full Working Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// mySaga.js
import {takeEvery, eventChannel} from 'redux-saga';
import {put, call, take} from 'redux-saga/effects';
import {INITIALIZE_WEB_SOCKETS_CHANNEL, WEBSOCKET_MESSAGE_RECEIVED} from '../actions';
function* createEventChannel(mySocket) {
return eventChannel(emit => {
mySocket.onmessage((message) => emit(message.data));
return () => {
mySocket.close();
};
});
}
function* initializeWebSocketsChannel() {
const mySocket = new WebSocket("ws://www.xyz.com/socketServer", "protocol");
const channel = yield call(createEventChannel, mySocket);
while (true) {
const {message} = yield take(channel);
yield put({type: WEBSOCKET_MESSAGE_RECEIVED, message});
}
}
export function* mySaga() {
yield [
takeEvery('INITIALIZE_WEB_SOCKETS_CHANNEL', initializeWebSocketsChannel)
];
}

Hope that helps !