Skip to main content
This is unreleased documentation for Yew Next version.
For up-to-date documentation, see the latest version on docs.rs.

yew_agent/worker/
hooks.rs

1use std::any::type_name;
2use std::fmt;
3use std::ops::Deref;
4use std::rc::Rc;
5
6use wasm_bindgen::prelude::*;
7use yew::prelude::*;
8
9use crate::utils::{BridgeIdState, OutputsAction, OutputsState};
10use crate::worker::provider::WorkerProviderState;
11use crate::worker::{Worker, WorkerBridge};
12
13/// Hook handle for the [`use_worker_bridge`] hook.
14pub struct UseWorkerBridgeHandle<T>
15where
16    T: Worker,
17{
18    inner: Rc<WorkerBridge<T>>,
19    ctr: UseReducerDispatcher<BridgeIdState>,
20}
21
22impl<T> UseWorkerBridgeHandle<T>
23where
24    T: Worker,
25{
26    /// Send an input to a worker agent.
27    pub fn send(&self, msg: T::Input) {
28        self.inner.send(msg);
29    }
30
31    /// Reset the bridge.
32    ///
33    /// Disconnect the old bridge and re-connects the agent with a new bridge.
34    pub fn reset(&self) {
35        self.ctr.dispatch(());
36    }
37}
38
39impl<T> Clone for UseWorkerBridgeHandle<T>
40where
41    T: Worker,
42{
43    fn clone(&self) -> Self {
44        Self {
45            inner: self.inner.clone(),
46            ctr: self.ctr.clone(),
47        }
48    }
49}
50
51impl<T> fmt::Debug for UseWorkerBridgeHandle<T>
52where
53    T: Worker,
54{
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        f.debug_struct(type_name::<Self>())
57            .field("inner", &self.inner)
58            .finish_non_exhaustive()
59    }
60}
61
62impl<T> PartialEq for UseWorkerBridgeHandle<T>
63where
64    T: Worker,
65{
66    fn eq(&self, rhs: &Self) -> bool {
67        self.inner == rhs.inner
68    }
69}
70
71/// A hook to bridge to a [`Worker`].
72///
73/// This hooks will only bridge the worker once over the entire component lifecycle.
74///
75/// Takes a callback as the argument.
76///
77/// The callback will be updated on every render to make sure captured values (if any) are up to
78/// date.
79#[hook]
80pub fn use_worker_bridge<T, F>(on_output: F) -> UseWorkerBridgeHandle<T>
81where
82    T: Worker + 'static,
83    F: Fn(T::Output) + 'static,
84{
85    let ctr = use_reducer(BridgeIdState::default);
86
87    let worker_state = use_context::<Rc<WorkerProviderState<T>>>()
88        .expect_throw("cannot find a provider for current agent.");
89
90    let on_output = Rc::new(on_output);
91
92    let on_output_clone = on_output.clone();
93    let on_output_ref = use_mut_ref(move || on_output_clone);
94
95    // Refresh the callback on every render.
96    {
97        let mut on_output_ref = on_output_ref.borrow_mut();
98        *on_output_ref = on_output;
99    }
100
101    let bridge = use_memo((worker_state, ctr.inner), |(state, _ctr)| {
102        state.create_bridge(Callback::from(move |output| {
103            let on_output = on_output_ref.borrow().clone();
104            on_output(output);
105        }))
106    });
107
108    UseWorkerBridgeHandle {
109        inner: bridge,
110        ctr: ctr.dispatcher(),
111    }
112}
113
114/// Hook handle for the [`use_worker_subscription`] hook.
115pub struct UseWorkerSubscriptionHandle<T>
116where
117    T: Worker,
118{
119    bridge: UseWorkerBridgeHandle<T>,
120    outputs: Vec<Rc<T::Output>>,
121    ctr: usize,
122}
123
124impl<T> UseWorkerSubscriptionHandle<T>
125where
126    T: Worker,
127{
128    /// Send an input to a worker agent.
129    pub fn send(&self, msg: T::Input) {
130        self.bridge.send(msg);
131    }
132
133    /// Reset the subscription.
134    ///
135    /// This disconnects the old bridge and re-connects the agent with a new bridge.
136    /// Existing outputs stored in the subscription will also be cleared.
137    pub fn reset(&self) {
138        self.bridge.reset();
139    }
140}
141
142impl<T> Clone for UseWorkerSubscriptionHandle<T>
143where
144    T: Worker,
145{
146    fn clone(&self) -> Self {
147        Self {
148            bridge: self.bridge.clone(),
149            outputs: self.outputs.clone(),
150            ctr: self.ctr,
151        }
152    }
153}
154
155impl<T> fmt::Debug for UseWorkerSubscriptionHandle<T>
156where
157    T: Worker<Output: fmt::Debug>,
158{
159    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
160        f.debug_struct(type_name::<Self>())
161            .field("bridge", &self.bridge)
162            .field("outputs", &self.outputs)
163            .finish_non_exhaustive()
164    }
165}
166
167impl<T> Deref for UseWorkerSubscriptionHandle<T>
168where
169    T: Worker,
170{
171    type Target = [Rc<T::Output>];
172
173    fn deref(&self) -> &[Rc<T::Output>] {
174        &self.outputs
175    }
176}
177
178impl<T> PartialEq for UseWorkerSubscriptionHandle<T>
179where
180    T: Worker,
181{
182    fn eq(&self, rhs: &Self) -> bool {
183        self.bridge == rhs.bridge && self.ctr == rhs.ctr
184    }
185}
186
187/// A hook to subscribe to the outputs of a [Worker] agent.
188///
189/// All outputs sent to current bridge will be collected into a slice.
190#[hook]
191pub fn use_worker_subscription<T>() -> UseWorkerSubscriptionHandle<T>
192where
193    T: Worker + 'static,
194{
195    let outputs = use_reducer(OutputsState::default);
196
197    let bridge = {
198        let outputs = outputs.clone();
199        use_worker_bridge::<T, _>(move |output| {
200            outputs.dispatch(OutputsAction::Push(Rc::new(output)))
201        })
202    };
203
204    {
205        let outputs_dispatcher = outputs.dispatcher();
206        use_effect_with(bridge.clone(), move |_| {
207            outputs_dispatcher.dispatch(OutputsAction::Reset);
208
209            || {}
210        });
211    }
212
213    UseWorkerSubscriptionHandle {
214        bridge,
215        outputs: outputs.inner.clone(),
216        ctr: outputs.ctr,
217    }
218}