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()
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,
158    T::Output: fmt::Debug,
159{
160    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
161        f.debug_struct(type_name::<Self>())
162            .field("bridge", &self.bridge)
163            .field("outputs", &self.outputs)
164            .finish()
165    }
166}
167
168impl<T> Deref for UseWorkerSubscriptionHandle<T>
169where
170    T: Worker,
171{
172    type Target = [Rc<T::Output>];
173
174    fn deref(&self) -> &[Rc<T::Output>] {
175        &self.outputs
176    }
177}
178
179impl<T> PartialEq for UseWorkerSubscriptionHandle<T>
180where
181    T: Worker,
182{
183    fn eq(&self, rhs: &Self) -> bool {
184        self.bridge == rhs.bridge && self.ctr == rhs.ctr
185    }
186}
187
188/// A hook to subscribe to the outputs of a [Worker] agent.
189///
190/// All outputs sent to current bridge will be collected into a slice.
191#[hook]
192pub fn use_worker_subscription<T>() -> UseWorkerSubscriptionHandle<T>
193where
194    T: Worker + 'static,
195{
196    let outputs = use_reducer(OutputsState::default);
197
198    let bridge = {
199        let outputs = outputs.clone();
200        use_worker_bridge::<T, _>(move |output| {
201            outputs.dispatch(OutputsAction::Push(Rc::new(output)))
202        })
203    };
204
205    {
206        let outputs_dispatcher = outputs.dispatcher();
207        use_effect_with(bridge.clone(), move |_| {
208            outputs_dispatcher.dispatch(OutputsAction::Reset);
209
210            || {}
211        });
212    }
213
214    UseWorkerSubscriptionHandle {
215        bridge,
216        outputs: outputs.inner.clone(),
217        ctr: outputs.ctr,
218    }
219}