An open API service indexing awesome lists of open source software.

https://github.com/maxzinchenko/react-use-socket

The package which makes web socket management much easier by using hooks.
https://github.com/maxzinchenko/react-use-socket

react react-use-socket socket websocket

Last synced: 2 months ago
JSON representation

The package which makes web socket management much easier by using hooks.

Awesome Lists containing this project

README

          

# React Use Socket

The package which makes web socket management much easier by using hooks.

The package is built over the WebSocket constructor from browser API.


npm version


size


npm downloads

---

## Structure

- [Installation](#installation)
- [One socket example](https://github.com/maxzinchenko/react-use-socket/blob/master/example/src/one-socket.tsx)
- [Multiple sockets example](https://github.com/maxzinchenko/react-use-socket/blob/master/example/src/many-sockets.tsx)
- [Provider options](#provider-options)
- [url](#url)
- [getRequestIndicator](#getRequestIndicator)
- [getResponseIndicator](#getResponseIndicator)
- [getError](#getError)
- [autoConnect](#autoConnect)
- [protocols](#protocols)
- [shouldReconnect](#shouldReconnect)
- [reconnectionIntervals](#reconnectionInterval)
- [serialize](#serialize)
- [deserialize](#deserialize)
- [debug](#debug)
- [Hooks usage](#hooks-usage)
- [useWebSocketState](#useWebSocketState)
- [useSignal](#useSignal)
- [useLazySignal](#useLazySignal)
- [useSubscription](#useSubscription)
- [useLazySubscription](#useLazySubscription)
- [Custom Error Type](#custom-error-type)
- [Provider options declaration](#provider-options-declaration)
- [Passing own types to WebSocketOptions type](#passing-own-types-to-websocketoptions-type)

---

## Installation

```
# using npm
npm install react-use-socket

# using yarn
yarn add react-use-socket
```

---

## Provider options

`WebSocketOptions`

| Name | Required | Type | Default |
| ------------------------------------------------- | -------- | --------------------------------------------------- | ----------- |
| [url](#url) | Yes | `string` | - |
| [getRequestIndicator](#getRequestIndicator) | Yes | `(req: Req) => string OR number` | - |
| [getResponseIndicator](#getResponseIndicator) | Yes | `(res: Res) => string OR number` | - |
| [getError](#getError) | Yes | `(res: Res) => string OR Err OR null` | - |
| [autoConnect](#autoConnect) | No | `boolean` | `true` |
| [protocols](#protocols) | No | `string OR string[]` | - |
| [shouldReconnect](#shouldReconnect) | No | `((event: CloseEvent) => boolean) OR boolean` | `true` |
| [reconnectionIntervals](#reconnectionInterval) | No | `number OR number[]` | `1000` |
| [serialize](#serialize) | No | `(req: Req) => SReq` | - |
| [deserialize](#deserialize) | No | `(res: Res) => DRes` | - |
| [debug](#debug) | No | `boolean` | - |

---

## url (required)

`string`

Url for the WebSocket constructor.

```ts
url: 'ws://localhost:3000'
```

```ts
url: 'wss://example.com'
```

## getRequestIndicator (required)

`(res: Res) => string | number`

**WARNING:** Make sure that the `getRequestIndicator(req)` value indicator is exactly same as `getResponseIndicator(res)`.

The package needs to know which the request received response belongs to.

Let us say that the request which needs to be sent to the API looks as:

**(this is just an example it's not a requirement to the API request type)**

```ts
req = {
get_user: {
id: 1
}
}
```

The indicator is `get_user` so the prop should be:

```ts
getRequestIndicator: req => Object.keys(req)[0]
```

## getResponseIndicator (required)

`(res: Res) => string | number`

**WARNING:** Make sure that the `getResponseIndicator(res)` value indicator is exactly same as `getRequestIndicator(req)`.

The package needs to know which the request received response belongs to.

Let us say that the response which comes from the API and needs to be handled looks as:

**(this is just an example it's not a requirement to the API response type)**

```ts
res = {
get_user: {
id: 1,
username: '@...',
avatarUrl: 'https://...'
}
}
```

The indicator is `get_user` so the prop should be:

```ts
getResponseIndicator: req => Object.keys(req)[0]
```

## getError (required)

`(res: Res) => string | Err | null`

Let us say that the failure response which comes from the API looks as:

**(this is just an example it's not a requirement to the API response type)**

```ts
res = {
get_user: {
error: 'Not found'
}
}
```

The error is `Not found` so the prop should be:

```ts
getError: res => res[Object.keys(req)[0]].error || null
```

When using custom error type `Err`:
(see doc of the [Custom error type](#Custom error type))
```ts
res = {
get_user: {
error: {
message: 'Not found',
meta: {...}
}
}
}
```

The error is an `object` so the prop should be:

```ts
getError: res => res[Object.keys(req)[0]].error || null
```

## autoConnect

`boolean` - (`true` by default)

When `true` you don't need to send anything to connect it.

When `false` you need to connect the socket manually by using `useWebSocketState` hook.

```ts
autoConnect: true
```

## shouldReconnect

`((event: CloseEvent) => boolean) | boolean` - (`true` by default)

When `true` the socket tries to reconnect if `event.code !== 1005`.

When the predicate is passed you are able to decide if the socket needs to be reconnected.

```ts
shouldReconnect: true
```

## debug

`boolean`

When `true` the package shows additional logs.

```ts
debug: ture
```

## protocols

`boolean`

Protocols for the WebSocket constructor.

```ts
protocols: 'some protocol'
```

```ts
protocols: ['some protocol']
```

## reconnectionInterval

`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.

```ts
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.

```ts
reconnectionInterval: [0, 1000, 2000, 3000, 4000, 5000, 10000]
```

## serialize

`(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.

```ts
serialize: req => {
return {
...req,
time: Date.now()
}
}
```

## deserialize

`(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.

```ts
deserialize: res => {
return res.data
}
```

---

## Custom Error Type

```ts
enum Socket {
MAIN = 'Main'
}

type Req = {
get_user: {
id: number
}
}

type DRes = {
get_user: {
username: string
avatarUrl: string
}
}

type Error = {
message: string
meta: {
timestamp: number
service: string
...
}
}
```

Putting these types into a generic hook:

```ts
const signalData = useSignal({...})
```

```ts
const [signalData, signalControls] = useLazySignal()
```

```ts
const [subscriptionData, subscriptionControls] = useSubscription('')
```

```ts
const [subscriptionData, subscriptionControls] = useLazySubscription('')
```

---

## Hooks usage

### useWebSocketState

When you use only one socket, passing the socket name is optional.

```tsx
import React from 'react';
import { useWebSocketState } from 'react-awesome-websocket';

const Component = () => {
const [connected, { open, close }] = useWebSocketState();

return (
<>

useWebSocketState example


Connected: {connected}


Open
Close
<>
);
};
```

When you use multiple sockets, passing the socket name is required. Otherwise, you get the
`The "name" is required for the hook usage` error.

```tsx
import React from 'react';
import { useWebSocketState } from 'react-awesome-websocket';

enum Socket {
MAIN = 'Main'
}

const Component = () => {
const [connected, { open, close }] = useWebSocketState({ name: Socket.MAIN });

return (
<>

useWebSocketState example


Connected: {connected}


Open
Close
<>
);
};
```

### useSignal

When you use only one socket, passing the socket name is optional.

```tsx
import React from 'react';
import { useSignal } from 'react-awesome-websocket';

type Req = {
get_user: {
id: number
}
}

type Res = {
get_user: {
username: string
avatarUrl: string
}
}

const Component = () => {
const { loading, error, data, mounted } = useSignal({
get_user: { id: 1 }
});

return (
<>

useSignal example


Loading: {loading}


Error: {error}


Mounted: {mounted}


Data:


{JSON.stringify(data, null, 4)}

<>
);
};
```

When you use multiple sockets, passing the socket name is required. Otherwise, you get the
`The "name" is required for the hook usage` error.

```tsx
import React from 'react';
import { useSignal } from 'react-awesome-websocket';

enum Socket {
MAIN = 'Main'
}

type Req = {
get_user: {
id: number
}
}

type Res = {
get_user: {
username: string
avatarUrl: string
}
}

const Component = () => {
const { loading, error, data, mounted } = useSignal({
get_user: { id: 1 }
}, { name: Socket.MAIN });

return (
<>

useSignal example


Loading: {loading}


Error: {error}


Mounted: {mounted}


Data:


{JSON.stringify(data, null, 4)}

<>
);
};
```

### useLazySignal

When you use only one socket, passing the socket name is optional.

```tsx
import React from 'react';
import { useLazySignal } from 'react-awesome-websocket';

type Req = {
get_user: {
id: number
}
}

type DRes = {
get_user: {
username: string
avatarUrl: string
}
}

const Component = () => {
const [signalData, { send }] = useLazySignal({
get_user: { id: 1 }
});

const { loading, error, data, mounted } = signalData;

const handleSendClick = () => {
send({ get_user: { id: 1 } });
}

return (
<>

useLazySignal example


Send Request

Loading: {loading}


Error:


{JSON.stringify(error, null, 4)}

Mounted: {mounted}


Data:


{JSON.stringify(data, null, 4)}

<>
);
};
```

When you use multiple sockets, passing the socket name is required. Otherwise, you get the
`The "name" is required for the hook usage` error.

```tsx
import React from 'react';
import { useLazySignal } from 'react-awesome-websocket';

enum Socket {
MAIN = 'Main'
}

type Req = {
get_user: {
id: number
}
}

type DRes = {
get_user: {
username: string
avatarUrl: string
}
}

const Component = () => {
const [signalData, { send }] = useLazySignal({
get_user: { id: 1 }
}, { name: Socket.MAIN });

const { loading, error, data, mounted } = signalData;

const handleSendClick = () => {
send({ get_user: { id: 1 } });
}

return (
<>

useLazySignal example


Send Request

Loading: {loading}


Error:


{JSON.stringify(error, null, 4)}

Mounted: {mounted}


Data:


{JSON.stringify(data, null, 4)}

<>
);
};
```

### useSubscription

When you use only one socket, passing the socket name is optional.

```tsx
import React from 'react';
import { useSubscription } from 'react-awesome-websocket';

type DRes = {
user_update: {
username: string
avatarUrl: string
}
}

const Component = () => {
const [{ data, error }, { stop }] = useSubscription('user_update');

return (
<>

useSubscription example


Stop subscription

Error: {error}


Data:


{JSON.stringify(data, null, 4)}

<>
);
};
```

When you use multiple sockets, passing the socket name is required. Otherwise, you get the
`The "name" is required for the hook usage` error.

```tsx
import React from 'react';
import { useSubscription } from 'react-awesome-websocket';

enum Socket {
MAIN = 'Main'
}

type DRes = {
user_update: {
username: string
avatarUrl: string
}
}

const Component = () => {
const [{ data, error }, { stop }] = useSubscription('user_update', {
name: Socket.MAIN
});
return (
<>

useSubscription example


Stop subscription

Error: {error}


Data:


{JSON.stringify(data, null, 4)}

<>
);
};
```

### useLazySubscription

When you use only one socket, passing the socket name is optional.

```tsx
import React from 'react';
import { useLazySubscription } from 'react-awesome-websocket';

type Req = {
get_user: {
id: number
}
}

type DRes = {
get_user: {
username: string
avatarUrl: string
}
}

const Component = () => {
const [{ data, error }, { start, stop }] = useLazySubscription('user_update');

return (
<>

useLazySubscription example


Start subscription
Stop subscription

Error: {error}


Data:


{JSON.stringify(data, null, 4)}

<>
);
};
```

When you use multiple sockets, passing the socket name is required. Otherwise, you get the
`The "name" is required for the hook usage` error.

```tsx
import React from 'react';
import { useLazySubscription } from 'react-awesome-websocket';

enum Socket {
MAIN = 'Main'
}

type Req = {
get_user: {
id: number
}
}

type DRes = {
get_user: {
username: string
avatarUrl: string
}
}

const Component = () => {
const [{ data, error }, { start, stop }] = useLazySubscription('user_update', {
name: Socket.MAIN
});

return (
<>

useLazySubscription example


Start subscription
Stop subscription

Error: {error}


Data:


{JSON.stringify(data, null, 4)}

<>
);
};
```

---

## Provider options declaration

```ts
import { WebSocketOptions } from 'react-awesome-websocket';

enum Scoket {
MAIN = 'Main'
};

type ScoketReq = {
method: string
data: Record
};

type SocketRes = {
[method: string]: Record
};

type ScoketSerializedReq = {
[method: string]: Record
};

type SocketDeserializedRes = Record;

type SocketError = {
message: string
meta: {}
};

const options: WebSocketOptions<
ScoketReq,
SocketRes,
Scoket,
SocketError,
ScoketSerializedReq,
SocketDeserializedRes
> = {
[Socket.MAIN]: {
url: 'ws://localhost:3000',
getRequestIndicator: req => req.method,
getResponseIndicator: res => Object.keys(res)[0],
getError: res => res[Object.keys(res)[0]].error_msg || null,

// serialize: (req: ScoketReq) => ScoketSerializedReq
serialize: ({ method, data }) => ({ [method]: data }),

// deserialize: (res: SocketRes) => SocketDeserializedRes
deserialize: (res: SocketRes) => res[Object.keys(res)[0]]
}
};
```

### Passing own types to WebSocketOptions type

`WebSocketOptions` is a generic type.

```ts
WebSocketOptions
```

`Req` - type of the socket request (required).

`Res` - type of the socket response (required).

`N` (default is `string`) - type of the sockets' names.

**This type should be passed into every hook if you need to use multiple sockets.**

`Err` (default is `string`) - type of the socket error which is reachable by using hooks as` error` (not 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 `WebSocketOptions.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 `WebSocketOptions.deserialize` function.**