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

yew_agent/worker/
provider.rs

1use std::any::type_name;
2use std::cell::RefCell;
3use std::fmt;
4use std::rc::Rc;
5
6use gloo_worker::Spawnable;
7use serde::{Deserialize, Serialize};
8use yew::prelude::*;
9
10use super::{Worker, WorkerBridge};
11use crate::reach::Reach;
12use crate::utils::get_next_id;
13use crate::{Bincode, Codec};
14
15/// Properties for [WorkerProvider].
16#[derive(Debug, Properties, PartialEq, Clone)]
17pub struct WorkerProviderProps {
18    /// The path to an agent.
19    pub path: AttrValue,
20
21    /// The reachability of an agent.
22    ///
23    /// Default: [`Public`](Reach::Public).
24    #[prop_or(Reach::Public)]
25    pub reach: Reach,
26
27    /// Lazily spawn the agent.
28    ///
29    /// The agent will be spawned when the first time a hook requests a bridge.
30    ///
31    /// Does not affect private agents.
32    ///
33    /// Default: `true`
34    #[prop_or(true)]
35    pub lazy: bool,
36
37    /// Children of the provider.
38    #[prop_or_default]
39    pub children: Html,
40}
41
42pub(crate) struct WorkerProviderState<W>
43where
44    W: Worker,
45{
46    id: usize,
47    spawn_bridge_fn: Rc<dyn Fn() -> WorkerBridge<W>>,
48    reach: Reach,
49    held_bridge: RefCell<Option<Rc<WorkerBridge<W>>>>,
50}
51
52impl<W> fmt::Debug for WorkerProviderState<W>
53where
54    W: Worker,
55{
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57        f.write_str(type_name::<Self>())
58    }
59}
60
61impl<W> WorkerProviderState<W>
62where
63    W: Worker,
64    W::Output: 'static,
65{
66    fn get_held_bridge(&self) -> Rc<WorkerBridge<W>> {
67        let mut held_bridge = self.held_bridge.borrow_mut();
68
69        match held_bridge.as_mut() {
70            Some(m) => m.clone(),
71            None => {
72                let bridge = Rc::new((self.spawn_bridge_fn)());
73                *held_bridge = Some(bridge.clone());
74                bridge
75            }
76        }
77    }
78
79    /// Creates a bridge, uses "fork" for public agents.
80    pub fn create_bridge(&self, cb: Callback<W::Output>) -> WorkerBridge<W> {
81        match self.reach {
82            Reach::Public => {
83                let held_bridge = self.get_held_bridge();
84                held_bridge.fork(Some(move |m| cb.emit(m)))
85            }
86            Reach::Private => (self.spawn_bridge_fn)(),
87        }
88    }
89}
90
91impl<W> PartialEq for WorkerProviderState<W>
92where
93    W: Worker,
94{
95    fn eq(&self, rhs: &Self) -> bool {
96        self.id == rhs.id
97    }
98}
99
100/// The Worker Agent Provider.
101///
102/// This component provides its children access to a worker agent.
103#[function_component]
104pub fn WorkerProvider<W, C = Bincode>(props: &WorkerProviderProps) -> Html
105where
106    W: Worker + 'static,
107    W::Input: Serialize + for<'de> Deserialize<'de> + 'static,
108    W::Output: Serialize + for<'de> Deserialize<'de> + 'static,
109    C: Codec + 'static,
110{
111    let WorkerProviderProps {
112        children,
113        path,
114        lazy,
115        reach,
116    } = props.clone();
117
118    // Creates a spawning function so Codec is can be erased from contexts.
119    let spawn_bridge_fn: Rc<dyn Fn() -> WorkerBridge<W>> = {
120        let path = path.clone();
121        Rc::new(move || W::spawner().encoding::<C>().spawn(&path))
122    };
123
124    let state = {
125        use_memo((path, lazy, reach), move |(_path, lazy, reach)| {
126            let state = WorkerProviderState::<W> {
127                id: get_next_id(),
128                spawn_bridge_fn,
129                reach: *reach,
130                held_bridge: Default::default(),
131            };
132
133            if *reach == Reach::Public && !*lazy {
134                state.get_held_bridge();
135            }
136            state
137        })
138    };
139
140    html! {
141        <ContextProvider<Rc<WorkerProviderState<W>>> context={state.clone()}>
142            {children}
143        </ContextProvider<Rc<WorkerProviderState<W>>>>
144    }
145}