1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
use std::cell::RefCell;
use std::rc::Rc;
use yew::prelude::*;
use crate::*;
/// State handle for [`use_bridge`] hook
pub struct UseBridgeHandle<T>
where
T: Bridged,
{
inner: Rc<RefCell<Box<dyn Bridge<T>>>>,
}
impl<T> UseBridgeHandle<T>
where
T: Bridged,
{
/// Send a message to an worker.
pub fn send(&self, msg: T::Input) {
let mut bridge = self.inner.borrow_mut();
bridge.send(msg);
}
}
/// A hook to bridge to an [`Worker`].
///
/// This hooks will only bridge the worker once over the entire component lifecycle.
///
/// Takes a callback as the only argument. The callback will be updated on every render to make
/// sure captured values (if any) are up to date.
///
/// # Examples
///
/// ```
/// # mod example {
/// use serde::{Deserialize, Serialize};
/// use yew::prelude::*;
/// use yew_agent::{use_bridge, UseBridgeHandle};
///
/// // This would usually live in the same file as your worker
/// #[derive(Serialize, Deserialize)]
/// pub enum WorkerResponseType {
/// IncrementCounter,
/// }
/// # mod my_worker_mod {
/// # use yew_agent::{HandlerId, Public, WorkerLink};
/// # use super::WorkerResponseType;
/// # pub struct MyWorker {
/// # pub link: WorkerLink<Self>,
/// # }
///
/// # impl yew_agent::Worker for MyWorker {
/// # type Input = ();
/// # type Output = WorkerResponseType;
/// # type Reach = Public<Self>;
/// # type Message = ();
/// #
/// # fn create(link: WorkerLink<Self>) -> Self {
/// # MyWorker { link }
/// # }
/// #
/// # fn update(&mut self, _msg: Self::Message) {
/// # // do nothing
/// # }
/// #
/// # fn handle_input(&mut self, _msg: Self::Input, id: HandlerId) {
/// # self.link.respond(id, WorkerResponseType::IncrementCounter);
/// # }
/// # }
/// # }
/// use my_worker_mod::MyWorker; // note that <MyWorker as yew_agent::Worker>::Output == WorkerResponseType
/// #[function_component(UseBridge)]
/// fn bridge() -> Html {
/// let counter = use_state(|| 0);
///
/// // a scoped block to clone the state in
/// {
/// let counter = counter.clone();
/// // response will be of type MyWorker::Output, i.e. WorkerResponseType
/// let bridge: UseBridgeHandle<MyWorker> = use_bridge(move |response| match response {
/// WorkerResponseType::IncrementCounter => {
/// counter.set(*counter + 1);
/// }
/// });
/// }
///
/// html! {
/// <div>
/// {*counter}
/// </div>
/// }
/// }
/// # }
/// ```
#[hook]
pub fn use_bridge<T, F>(on_output: F) -> UseBridgeHandle<T>
where
T: Bridged,
F: Fn(T::Output) + 'static,
{
let on_output = Rc::new(on_output);
let on_output_clone = on_output.clone();
let on_output_ref = use_mut_ref(move || on_output_clone);
// Refresh the callback on every render.
{
let mut on_output_ref = on_output_ref.borrow_mut();
*on_output_ref = on_output;
}
let bridge = use_mut_ref(move || {
T::bridge({
Rc::new(move |output| {
let on_output = on_output_ref.borrow().clone();
on_output(output);
})
})
});
UseBridgeHandle { inner: bridge }
}
impl<T: Worker> Clone for UseBridgeHandle<T> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}