Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

use_websocket

Creating and managing a Websocket connection.

Demo

Usage

Values are (en)decoded via the given codec. You can use any of the codecs, string or binary.

Please check the codec chapter to see what codecs are available and what feature flags they require.

use leptos::prelude::*;
use codee::string::FromToStringCodec;
use leptos_use_websocket::{use_websocket, UseWebSocketReturn};
use leptos_use_core::ConnectionReadyState;

#[component]
fn Demo() -> impl IntoView {
let UseWebSocketReturn {
    ready_state,
    message,
    send,
    open,
    close,
    ..
} = use_websocket::<String, String, FromToStringCodec>("wss://echo.websocket.events/");

let send_message = move |_| {
    send(&"Hello, world!".to_string());
};

let status = move || ready_state.get().to_string();

let connected = move || ready_state.get() == ConnectionReadyState::Open;

let open_connection = move |_| {
    open();
};

let close_connection = move |_| {
    close();
};

view! {
    <div>
        <p>"status: " {status}</p>

        <button on:click=send_message disabled=move || !connected()>"Send"</button>
        <button on:click=open_connection disabled=connected>"Open"</button>
        <button on:click=close_connection disabled=move || !connected()>"Close"</button>

        <p>"Receive message: " {move || format!("{:?}", message.get())}</p>
    </div>
}
}

Here is another example using msgpack for encoding and decoding. This means that only binary messages can be sent or received. For this to work you have to enable the msgpack_serde feature flag.

use leptos::*;
use codee::binary::MsgpackSerdeCodec;
use leptos_use_websocket::{use_websocket, UseWebSocketReturn};
use serde::{Deserialize, Serialize};

#[component]
fn Demo() -> impl IntoView {
#[derive(Serialize, Deserialize)]
struct SomeData {
    name: String,
    count: i32,
}

let UseWebSocketReturn {
    message,
    send,
    ..
} = use_websocket::<SomeData, SomeData, MsgpackSerdeCodec>("wss://some.websocket.server/");

let send_data = move || {
    send(&SomeData {
        name: "John Doe".to_string(),
        count: 42,
    });
};

view! {}
}

Heartbeats

Heartbeats can be configured by the heartbeat option. You have to provide a heartbeat type, that implements the Default trait and an Encoder for it. This encoder doesn’t have to be the same as the one used for the other websocket messages.

use leptos::*;
use codee::string::FromToStringCodec;
use leptos_use_websocket::{use_websocket_with_options, UseWebSocketOptions, UseWebSocketReturn};
use serde::{Deserialize, Serialize};

#[component]
fn Demo() -> impl IntoView {
#[derive(Default)]
struct Heartbeat;

// Simple example for usage with `FromToStringCodec`
impl std::fmt::Display for Heartbeat {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "<Heartbeat>")
    }
}

let UseWebSocketReturn {
    send,
    message,
    ..
} = use_websocket_with_options::<String, String, FromToStringCodec, _, _>(
    "wss://echo.websocket.events/",
    UseWebSocketOptions::default()
        // Enable heartbeats every 10 seconds. In this case we use the same codec as for the
        // other messages. But this is not necessary.
        .heartbeat::<Heartbeat, FromToStringCodec>(10_000),
);

view! {}
}

Relative Paths

If the provided url is relative, it will be resolved relative to the current page. Urls will be resolved like this the following. Please note that the protocol (http vs https) will be taken into account as well.

Current PageRelative UrlResolved Url
http://example.com/some/where/api/wsws://example.com/api/ws
https://example.com/some/where/api/wswss://example.com/api/ws
https://example.com/some/whereapi/wswss://example.com/some/where/api/ws
https://example.com/some/where//otherdomain.com/api/wswss://otherdomain.com/api/ws

Usage with provide_context

The return value of use_websocket utilizes several type parameters which can make it cumbersome to use with provide_context + expect_context. The following example shows how to avoid type parameters with dynamic dispatch. This sacrifices a little bit of performance for the sake of ergonomics. However, compared to network transmission speeds this loss of performance is negligible.

First we define the struct that is going to be passed around as context.

use leptos::prelude::*;
use std::sync::Arc;

#[derive(Clone)]
pub struct WebsocketContext {
    pub message: Signal<Option<String>>,
    send: Arc<dyn Fn(&String)>,  // use Arc to make it easily cloneable
}

impl WebsocketContext {
    pub fn new(message: Signal<Option<String>>, send: Arc<dyn Fn(&String)>) -> Self {
        Self {
            message,
            send,
        }
    }

    // create a method to avoid having to use parantheses around the field
    #[inline(always)]
    pub fn send(&self, message: &str) {
        (self.send)(&message.to_string())
    }
}

Now you can provide the context like the following.

use leptos::prelude::*;
use codee::string::FromToStringCodec;
use leptos_use_websocket::{use_websocket, UseWebSocketReturn};
use std::sync::Arc;
#[derive(Clone)]
pub struct WebsocketContext {
    pub message: Signal<Option<String>>,
    send: Arc<dyn Fn(&String) + Send + Sync>,
}

impl WebsocketContext {
    pub fn new(message: Signal<Option<String>>, send: Arc<dyn Fn(&String) + Send + Sync>) -> Self {
        Self {
            message,
            send,
        }
    }
}

#[component]
fn Demo() -> impl IntoView {
let UseWebSocketReturn {
    message,
    send,
    ..
} = use_websocket::<String, String, FromToStringCodec>("ws:://some.websocket.io");

provide_context(WebsocketContext::new(message, Arc::new(send.clone())));

view! {}
}

Finally let’s use the context:

use leptos::prelude::*;
use leptos_use_websocket::{use_websocket, UseWebSocketReturn};
use std::sync::Arc;
#[derive(Clone)]
pub struct WebsocketContext {
    pub message: Signal<Option<String>>,
    send: Arc<dyn Fn(&String)>,
}

impl WebsocketContext {
    #[inline(always)]
    pub fn send(&self, message: &str) {
        (self.send)(&message.to_string())
    }
}

#[component]
fn Demo() -> impl IntoView {
let websocket = expect_context::<WebsocketContext>();

websocket.send("Hello World!");

view! {}
}

SendWrapped Return

The returned closures open, close, and send are sendwrapped functions. They can only be called from the same thread that called use_websocket.

Server-Side Rendering

Make sure you follow the instructions in Server-Side Rendering.

On the server the returned functions amount to no-ops.

Installation

cargo add leptos-use-websocket

Options

UseWebSocketOptions

Options for use_websocket_with_options.

Every option is set with a builder method of the same name, e.g. UseWebSocketOptions::default().heartbeat(...). See also the API docs.

heartbeat

Type: Option<HeartbeatOptions<Hb, HbCodec>>

Heartbeat options

on_open

Type: Arc<dyn Fn(Event) + Send + Sync>

WebSocket connect callback.

on_message

Type: Arc<dyn Fn(&Rx) + Send + Sync>

WebSocket message callback for typed message decoded by codec.

on_message_raw

Type: Arc<dyn Fn(&str) + Send + Sync>

WebSocket message callback for text.

on_message_raw_bytes

Type: ArcFnBytes

WebSocket message callback for binary.

on_error

Type: Arc<dyn Fn(UseWebSocketError<E, D>) + Send + Sync>

WebSocket error callback.

on_close

Type: Arc<dyn Fn(CloseEvent) + Send + Sync>

WebSocket close callback.

reconnect_limit

Type: ReconnectLimit

Retry times. Defaults to ReconnectLimit::Limited(3). Use ReconnectLimit::Infinite for infinite retries.

reconnect_interval

Type: u64

Retry interval in ms. Defaults to 3000.

immediate

Type: bool

If true the WebSocket connection will immediately be opened when calling this function. If false you have to manually call the open function. Defaults to true.

protocols

Type: Signal<Option<Vec<String>>>

Sub protocols. See MDN Docs.

Can be set as a signal to support protocols only available after the initial render.

Note that protocols are only updated on the next websocket open() call, not whenever the signal is updated. Therefore “lazy” protocols should use the immediate(false) option and manually call open().

Types

Source

Source • Demo • Docs