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

yew_agent/reactor/
provider.rs

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