redux-ws-middleware
v1.0.8
Published
This package makes web socket management much easier with redux
Downloads
1,284
Maintainers
Readme
Redux WebSocket Middleware
This package makes web socket management much easier with redux. The package is built over the WebSocket constructor from browser API.
Examples
Structure
Installation
# using npm
npm install redux-ws-middleware
# using yarn
yarn add redux-ws-middleware
Options
| Name | Required | Type | Default |
| ---------------------------------------------- | -------- | --------------------------------------------------- | ----------- |
| url | Yes | string
| - |
| actionTypes | Yes | Array<string OR RegExp>
| - |
| completedActionTypes | Yes | Array<string>
| - |
| onMessage | Yes | (res: Res, dispatch: Dispatch<AnyAction>) => void
| - |
| autoConnect | No | boolean
| true
|
| protocols | No | string OR string[]
| - |
| shouldReconnect | No | ((event: CloseEvent) => boolean) OR boolean
| true
|
| reconnectionIntervals | No | number OR number[]
| 1000
|
| shouldOpen | No | ((req: Req) => boolean) OR boolean
| false
|
| shouldClose | No | ((res: DRes) => boolean) OR boolean
| false
|
| serialize | No | (req: Req) => SReq
| - |
| deserialize | No | (res: Res) => DRes
| - |
| debug | No | boolean
| - |
url
Required*
Type: string
Url for the WebSocket constructor.
url: 'ws://localhost:3000'
url: 'wss://example.com'
actionTypes
Required*
Type: [RegExp | string, RegExp | string, RegExp | string]
WARNING: Sequence is important!
Types that you are able to manage the socket with. You can have socket dispatching any of them.
The first element should be the SEND
action type.
Second - CONNECT
type.
Third - DISCONNECT
type.
IMPORTANT: Do not use /g
at the end of RegExp!
actionTypes: ['SEND', 'CONNECT', 'DISCONNECT']
actionTypes: [new RegExp(/_REQUEST$/), 'CONNECT', 'DISCONNECT']
If you don't need these: CONNECT
, DISCONNECT
so just don't send them.
actionTypes: ['SEND', 'CONNECT']
actionTypes: ['SEND']
actionTypes: [new RegExp(/_REQUEST$/)]
completedActionTypes
Required*
Type: [string, string]
WARNING: Sequence is important!
Types that you receive back on actions.
completedActionTypes: ['CONNECTED', 'DISCONNECTED']
onMessage
Required*
Type: (res: Res, dispatch: Dispatch<AnyAction>) => void
The callback gets called with deserialized data already, if you put deserialize function into options, or with a normal data if you don't. And with a dispatch
so you can manage your store.
*(this is just an example of the onMessage
handler)
onMessage: (data, dispatch) => {
switch (data.method) {
case 'posts':
if (data.error) {
dispatch(postsActions.getPostsRejected(data.error));
} else {
dispatch(postsActions.getPostsFulfilled(data.result));
}
break;
...
default:
break;
}
}
autoConnect
Type: boolean
- (true
by default)
When true
you don't need to send anything else to connect it.
When false
you need to dispatch the connect action with a type actionTypes[1]
.
autoConnect: false
debug
Type: boolean
When true
the package shows additional logs.
debug: ture
protocols
Type: string | string[]
Protocols for the WebSocket constructor.
protocols: 'some protocol'
protocols: ['some protocol']
shouldReconnect
Type: ((event: CloseEvent) => boolean) | boolean
- (true
by default)
When true
the socket tries to reconnect if event.code !== 1005
.
When predicate is passed you are able to decide if the socket needs to be reconnected.
shouldReconnect: false
reconnectionInterval
Type: number | number[]
- (1000
by default)
In milliseconds. When array each new connection uses the next number from the array for a timeout to avoid DDOSing a server.
reconnectionInterval: 1000
When reconnection count reaches the last array element it uses it each the next time.
When the socket connects back the next reconnection loop will start from the 0
index.
reconnectionInterval: [0, 1000, 2000, 3000, 4000, 5000, 10000]
shouldOpen
Type: ((req: Req) => boolean) | boolean
- (false
by default)
Req is a template of the generic MiddlewareOptions type
When true
the socket opens on any send
action if connection is closed`.
When predicate is passed you are able to decide if socket needs to be open.
shouldOpen: true
When predicate is passed you are able to decide when socket needs to be open.
shouldOpen: (req: SomeReq) => req.method === 'load_session'
shouldClose
Type: ((res: DRes) => boolean) | boolean
- (false
by default)
DRes
is a templates of the generic MiddlewareOptions
type
When true
the socket closes connection after each response from the server.
When predicate is passed you are able to decide when the socket needs to be closed.
shouldClose: true
shouldClose: (res: SomeDeserializedRes) => res.method === 'logout'
serialize
Type: (req: Req) => SReq
Req
and SReq
are templates of the generic MiddlewareOptions
type
The format function gets called to prepare the data to get submitted to the server. For example, camelCase
to snake_case
conversion.
serialize: req => {
return {
...req,
time: Date.now()
}
}
deserialize
Type: (res: Res) => DRes
Res
and DRes
are templates of the generic MiddlewareOptions
type
The format function gets called to prepare the message to get submitted to the onMessage
callback. For example, snake_case
to camelCase
conversion.
deserialize: res => {
return res.data
}
Usage
Connecting
const SOCKET_SEND = 'SCOKET_SEND';
const SOCKET_CONNECT = 'SOCKET_CONNECT';
const SOCKET_DISCONNECT = 'SOCKET_DISCONNECT';
const otpions = {
...
actionTypes: [SOCKET_SEND, SOCKET_CONNECT, SOCKET_DISCONNECT],
...
};
const connectAction = () => ({ type: SOCKET_CONNECT });
dispatch(connectAction());
Disconnecting
import { CloseAction } from 'redux-ws-middleware';
const SOCKET_SEND = 'SCOKET_SEND';
const SOCKET_CONNECT = 'SOCKET_CONNECT';
const SOCKET_DISCONNECT = 'SOCKET_DISCONNECT';
const otpions = {
...
actionTypes: [SOCKET_SEND, SOCKET_CONNECT, SOCKET_DISCONNECT],
...
};
const disconnectAction = (code?: number): CloseAction<typeof SOCKET_DISCONNECT> => ({
type: SOCKET_DISCONNECT,
payload: { code }
});
OR
dispatch(disconnectAction(1000));
The disconnectAction
can return:
{
type: SOCKET_DISCONNECT,
code
}
{
type: SOCKET_DISCONNECT,
payload: { code }
}
{
type: SOCKET_DISCONNECT,
data: { code }
}
(all these are supported by CloseAction<typeof SOCKET_DISCONNECT>
type)
Sending data
The data can be sent in payload
OR in data
key.
import { SendAction } from 'redux-ws-middleware';
const GET_POSTS = 'GET_POSTS_REQUEST';
const otpions = {
...
actionTypes: [new RegExp(/_REQUEST$/)],
...
}
const getPostsAction = (offset: number, limit: number): SendAction<typeof GET_POSTS> => ({
type: GET_POSTS,
payload: { offset, limit }
});
dispatch(getPostsAction(0, 20));
The getPostsAction
can return:
{
type: GET_POSTS,
payload: { code }
}
{
type: GET_POSTS,
data: { code }
}
(all these are supported by SendAction<typeof GET_POSTS>
type)
MiddlewareOptions declaration
import { createSocketMiddleware, MiddlewareOptions } from 'redux-ws-middleware';
type ScoketReq = {
method: string
data: Record<string, unknown>
};
type SocketRes = {
[method: string]: Record<string, unknown>
};
type ScoketSerializedReq = {
[method: string]: Record<string, unknown>
};
type SocketDeserializedRes = Record<string, unknown>;
const options: MiddlewareOptions<ScoketReq, SocketRes, ScoketSerializedReq, SocketDeserializedRes> = {
url: 'ws://localhost:3000',
actionTypes: ['SEND', 'CONNECT', 'DISCONNECT'],
completedActionTypes: ['CONNECTED', 'DISCONNECTED'],
// serialize: (req: ScoketReq) => ScoketSerializedReq
serialize: ({ method, data }) => ({ [method]: data }),
// deserialize: (res: SocketRes) => SocketDeserializedRes
deserialize: (res: SocketRes) => res[Object.keys(res)[0]]
};
const socketMiddleware = createSocketMiddleware(options);
Passing own types to MiddlewareOptions type
MiddlewareOptions
is a generic type.
MiddlewareOptions<Req, Res, SReq = Req, DRes = Res>
Req
- type of the socket request (required).
Res
- type of the socket response (required).
SReq
(default is Req
) - type of serialized socket request which will be sent to the API (not required).
This type should be returned from the MiddlewareOptions.serialize function.
DRes
(default is Res
) - type of deserialized socket response which is reachable by using hooks as data
(not required).
This type should be returned from the MiddlewareOptions.deserialize function.