Skip to main content
Version: 5.x.x

Socket Listener


Introduction

Listener is a class that creates a template for receiving events from server. Its strength is its strict and predictable data structure which together with typescript gives a lot of confidence while developing data exchange features.

To use the listener we need to trigger the listen method with provided callback.


Purpose

  • Configures events listeners templates
  • Standardizes the system’s data schema
  • Listen to server events

Initialization

Listener should be initialized from the Socket instance with createListener method. This passes a shared reference to the place that manages communication in the application.

import { socket } from "./socket";

export const onChatMessage = socket.createListener<ChatMessageType>()({
endpoint: "chat-message", // endpoint of the event
});

export const onUserStatusChange = socket.createListener<UserStatusType>()({
endpoint: "status", // endpoint of the event
});

Usage

Listening

Use the listen method to start the listening for incoming events.

onChatMessage.listen({
callback: (message) => {
// message have type ChatMessageType
console.log(message);
},
});

Remove listener

Use the returned method to stop listening.

const removeEvent = onChatMessage.listen({ callback: (message) => console.log(message) });

...

removeEvent() // We remove listener

Remove event listeners directly from the adapter

const callback = (message) => console.log(message);
const removeEvent = onChatMessage.listen({ callback });

...

socket.adapter.listeners.delete(onChatMessage.endpoint) // We remove ALL listeners from given event
socket.adapter.listeners.get(onChatMessage.endpoint).delete(callback) // We remove single listener from given event

Listen to event once

const removeEvent = onChatMessage.listen({
callback: (message) => {
console.log(message);
removeEvent(); // We listen only to single event
},
});

Listening with onData hook

You can use onData method to listen to all events of given listener. This method is useful when you want to attach some logic to any occurrence of the event. It's additional way of hooking into the listener groups.

export const onNoteChange = socket
.createListener<NoteData>()({
endpoint: "notes/:id",
})
.onData((event) => {
// We will receive all events from given listener,
// no mater of ID, as it will be filled later on while listening
console.log(event);
});

onNoteChange.setParams({ noteId: 1 }).listen(() => {
// some logic
});

onNoteChange.setParams({ noteId: 2 }).listen(() => {
// some logic
});

// console.log(event) ===> { noteId: 1, ... }
// console.log(event) ===> { noteId: 2, ... }

Integration with Hyper Fetch

We can use this to make simple integration with Hyper Fetch. We can use onData hook to update the cache with new data from the socket.


// ==> Our core setup
const client = new Client({ url: "http://localhost:3000" });
const getNote = client.createRequest()({
endpoint: "/note/:noteId";
})


// ==> Our realtime listener

export const onNoteChange = socket.createListener<NoteData>()({
endpoint: "notes",
}).onData((event) => {
const { noteId } = event;

// Fill the params so our cache can find the request in storage
const noteRequest = client.cache.get(getNote.setParams({ noteId }));

// Update the cache
client.cache.update(noteRequest, (prevData) => {
return {...prevData, data: event}
});
});


Methods

Using methods on a listener is different from other classes in Hyper Fetch. This is to ensure isolation between different uses, which allows you to avoid overwriting previously-prepared listeners and to dynamically generate keys for queues or the cache.

danger

Using any method on listener returns its clone! We don't return a reference!

// ❌ WRONG

const listener = getChatMessageListener;

listener.setOptions({ ... }); // Returns CLONED listener with assigned options

listener.listen(); // Error - no options will be applied
// ✅ Good

const listener = getChatMessageListener;

const listenerWithOptions = listener.setOptions({ ... }); // Returns CLONED listener with assigned options

listenerWithOptions.listen(); // Success - options applied

Parameters

Configuration options

{
endpoint: Endpoint;
options: T extends SocketAdapterType<any, any, infer O, any> ? O : never;
params: string extends T ? NegativeTypes : (T extends ${string}:,${infer Param}/,${infer Rest} ? [k in Param | keyof ExtractRouteParams<Rest>]: ParamType : (T extends ${string}:,${infer Param} ? [k in Param]: ParamType : NegativeTypes));
}