yew
yew copied to clipboard
Error: cannot find a provider for current agent
Problem when trying to make a reactor agent i get the above error message after the code compiled successfully
Steps To Reproduce Steps to reproduce the behavior: All code and full error message attached below
Expected behavior the code runs after compiling with no runtime errors
Screenshots
Environment:
- Yew version: v0.21
- Rust version: v1.82.0-nightly
- Target, if relevant: wasm32-unknown-unknown
- Build tool, if relevant: trunk
- OS, if relevant: linux x86_64
- Browser and version, if relevant: brave v1.67.119
Questionnaire
- [ ] I'm interested in fixing this myself but don't know where to start
- [ ] I would like to fix and I have a solution
- [ ] I don't have time to fix this right now, but maybe later
use std::{borrow::Cow, sync::Arc};
use futures::{lock::Mutex, SinkExt, StreamExt};
use reqwasm::websocket::{futures::WebSocket, Message};
use serde::{Deserialize, Serialize};
use wasm_bindgen_futures::spawn_local;
use yew::{function_component, hook, html, use_effect_with, use_node_ref, use_state, use_state_eq, BaseComponent, Callback, Event, Html, Properties};
use yew_agent::{oneshot::OneshotProvider, prelude::use_oneshot_runner, reactor::{reactor, use_reactor_bridge, ReactorEvent, ReactorScope}};
use crate::components::{navbar::Navbar, button::Button};
#[derive(Properties, PartialEq, Default)]
pub struct ChatProps {
messages: Vec<PublicMessage>
}
#[function_component(Chat)]
pub fn chat(props: &ChatProps) -> Html {
let messages = Arc::new(use_state_eq(|| props.messages.clone() ));
let messages_reactor = messages.clone();
let reactor = use_reactor_bridge(move |output: ReactorEvent<ChatReactor>| match output {
ReactorEvent::Output(msg) => {
let mut msg_list: Vec<PublicMessage> = messages_reactor.to_vec();
msg_list.push(msg);
messages_reactor.set(msg_list);
}
_ => {}
});
let submit = Callback::from(move |_| {
let _ = reactor.send(String::new());
});
html! {
<>
<Navbar user_id={ None }/>
<div class="messenger-page-container">
<div class="messenger-main-section" id="messages">
{
messages.clone().iter().map(|m| {
html! {
<div class="message-container" id={ m.message_id.to_string() }>
<img class="message-avatar" src={ format!("/userassets/avatar/{}.png", m.user_id)}/>
<div class="message-outer">
<div class="message-inner">
<div class="message-header">
<div class="message-username">{ m.display_name.clone() }</div>
<div class="message-date"> { m.created_at } </div>
</div>
<div class="message-body">{ m.content.clone() }</div>
</div>
</div>
</div>
}
}).collect::<Html>()
}
</div>
<div class="messenger-end-marker">{ "Already At Newest Messages" }</div>
</div>
<div class="messenger-entry-container" style="width: 100%; position: absolute; bottom: 0px; align-items: center; display: flex; flex-direction: column">
<div class="messenger-main-section">
<div class="ui-horizontal-menu">
<img src="/static/icons/messenger.svg" style="padding: 0px 10px"/>
<input style="width: 100%;" class="ui-input" id="inputField" type="text" placeholder="Send Message"/>
<button class="ui-button" id="enterButton" onclick={ submit }>
<img src="/static/icons/return.svg"/>
</button>
</div>
</div>
</div>
</>
}
}
#[reactor(ChatReactor)]
pub async fn chat_reactor(mut scope: ReactorScope<String, PublicMessage>) {
let arcscope_rx = Arc::new(Mutex::new(scope));
let arcscope_tx = Arc::clone(&arcscope_rx);
let ws = WebSocket::open("ws://localhost:8000/messenger/connect/1").unwrap();
let (mut ws_sender, mut ws_receiver) = ws.split();
spawn_local(async move {
while let Some(msg) = ws_receiver.next().await {
match msg {
Ok(Message::Text(data)) => {
log::debug!("{}", data);
let message: PublicMessage = serde_json::from_str(&data).unwrap();
arcscope_tx.lock().await.send(message).await.unwrap();
}
Ok(Message::Bytes(b)) => {
if let Ok(data) = String::from_utf8(b) {
log::debug!("{}", data);
let message: PublicMessage = serde_json::from_str(&data).unwrap();
arcscope_tx.lock().await.send(message).await.unwrap();
}
},
Err(e) => {
log::error!("{}", e);
}
}
}
});
spawn_local(async move {
while let Some(msg) = arcscope_rx.lock().await.next().await {
if let Err(e) = ws_sender.send(Message::Text(serde_json::to_string(&msg).unwrap())).await {
log::error!("{}", e);
}
}
log::debug!("Websocket connection closed");
});
}
#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
pub struct PublicMessage {
pub message_id: i32,
pub user_id: i32,
pub display_name: String,
pub created_at: i64,
pub content: String,
}
just to add to this. the exact point where the error seems to occur is when i create a reactor using the use_reactor_bridge method. removing all the logic from my actual reactor and simply setting a catch all case that does nothing
let reactor = use_reactor_bridge(move |output: ReactorEvent<ChatReactor>| match output {
_ => {}
});
#[reactor(ChatReactor)]
pub async fn chat_reactor(mut scope: ReactorScope<String, PublicMessage>) {}
the above code still results in the same error
For posterity, I just came across a similar issue, and found the resolution.
On the Yew Discord, this discussion gave me the answer: link only works if you're on the Discord server
Where it refers to this code:
#[function_component]
pub fn App() -> Html {
html! {
<OneshotProvider<FibonacciTask, Postcard> path="/worker.js">
<Main />
</OneshotProvider<FibonacciTask, Postcard>>
}
}
Basically, we need to wrap our applications in the providers. My code looks like this after the fix:
#[function_component(App)]
pub fn app() -> Html {
html! {
<ReactorProvider<WebSocketReactor> path="/services/game_service.js">
<BrowserRouter>
<Switch<Route> render={switch} />
</BrowserRouter>
</ReactorProvider<WebSocketReactor>>
}
}
<ReactorProvider/> has to exist somewhere in the ancestor nodes.