WebSockets impossible to use in Yew
voidpumpkin opened this issue · 4 comments
WebSockets impossible to use in Yew for me because I just did not manage to
- store active connection in use_state/use_mut_ref in a way where i can deref/convert into a owned value.
write.send(self)
moves the connection instead of just taking a mutable ref - same problem as above with a struct component
- when using
futures::mspc:channel
I almost made it work but with it I needed to keep poling for messages which imo already is bad
So either make the WebSocket struct clonable or make .send work with a &mut self instead of just self
Until then plain old web_sys works: https://gist.github.com/JIuH4/9b990f7da7d12419f3f3a4a01ee9a9d7
So either make the WebSocket struct clonable
It used to be that way but was changed because that caused bug(s). See 445e9a5
I'll try to get a write up working example soon
@voidpumpkin I wrote the following code, which compiles and works:
use std::ops::DerefMut;
use futures::{sink::SinkExt, stream::StreamExt};
use reqwasm::websocket::futures::WebSocket;
use reqwasm::websocket::Message;
use wasm_bindgen::UnwrapThrowExt;
use yew::prelude::*;
#[function_component]
fn App() -> Html {
let sender = use_mut_ref(|| None);
let receiver = use_mut_ref(|| None);
{
use_effect_with_deps(
move |_| {
let ws = WebSocket::open("ws://localhost:8080/api/ws").unwrap_throw();
let (tx, rx) = ws.split();
sender.borrow_mut().replace(tx);
receiver.borrow_mut().replace(rx);
{
wasm_bindgen_futures::spawn_local(async move {
let mut sender = sender.borrow_mut();
let sender = sender.deref_mut().as_mut().unwrap();
match sender.send(Message::Text("msg".to_string())).await {
Ok(_) => gloo_console::log!("sent"),
Err(e) => gloo_console::error!(format!("sending error: {}", e)),
}
});
}
{
wasm_bindgen_futures::spawn_local(async move {
let mut receiver = receiver.borrow_mut();
let receiver = receiver.deref_mut().as_mut().unwrap();
while let Some(val) = receiver.next().await {
gloo_console::console!(format!("{:?}", val));
}
});
}
|| {}
},
(),
);
}
html! {"hello world"}
}
fn main() {
yew::start_app::<App>();
}
There's some ugly aspects of it but those are because futures and Yew don't really play well together.
Well thx.
Personally i still prefer just using web-sys.
But this issue is closable now.
Here's a more cleaner and adaptable example:
use futures::channel::mpsc::{UnboundedReceiver, UnboundedSender};
use futures::{future, pin_mut};
use futures::{sink::SinkExt, stream::StreamExt};
use reqwasm::websocket::futures::WebSocket;
use reqwasm::websocket::{Message, WebSocketError};
use std::cell::RefCell;
use std::ops::DerefMut;
use std::rc::Rc;
use wasm_bindgen::UnwrapThrowExt;
use yew::prelude::*;
#[derive(Clone)]
pub struct Context {
sender: UnboundedSender<Message>,
receiver: Rc<RefCell<UnboundedReceiver<Result<Message, WebSocketError>>>>,
}
impl PartialEq for Context {
fn eq(&self, _other: &Self) -> bool {
false
}
}
#[function_component]
fn Consumer() -> Html {
let ctx: Context = use_context().unwrap();
{
use_effect_with_deps(
move |_| {
let mut tx = ctx.sender;
let rx = ctx.receiver;
wasm_bindgen_futures::spawn_local(async move {
tx.send(Message::Text("message".to_string())).await.unwrap();
});
wasm_bindgen_futures::spawn_local(async move {
while let Some(m) = rx.borrow_mut().deref_mut().next().await {
gloo_console::console!(format!("{:?}", m));
}
});
|| ()
},
(),
)
}
html! {"hello world"}
}
#[function_component]
fn App() -> Html {
let (mut read_tx, read_rx) = futures::channel::mpsc::unbounded();
let (write_tx, write_rx) = futures::channel::mpsc::unbounded();
let context = Context {
sender: write_tx,
receiver: Rc::new(RefCell::new(read_rx)),
};
{
use_effect_with_deps(
move |_| {
let ws = WebSocket::open("ws://localhost:8080/api/ws").unwrap_throw();
let (write, mut read) = ws.split();
let fwd_writes = write_rx.map(Ok).forward(write);
let fwd_reads = async move {
while let Some(m) = read.next().await {
read_tx.send(m).await.unwrap()
}
};
wasm_bindgen_futures::spawn_local(async move {
pin_mut!(fwd_writes, fwd_reads);
future::select(fwd_writes, fwd_reads).await;
});
|| {}
},
(),
);
}
html! {
<ContextProvider<Context> {context}>
<Consumer />
</ContextProvider<Context>>
}
}
fn main() {
yew::start_app::<App>();
}