Feat: fix internal lifecycle
This commit is contained in:
1 parent
edf09c1892
commit
5204862bc2
6 files changed
+152
-182
No files matched your search
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"rust-analyzer.inlayHints.enable": false
|
"rust-analyzer.inlayHints.enable": true
|
||||||
}
|
}
|
||||||
@@ -10,6 +10,9 @@ pub enum Error {
|
|||||||
#[error("Wrong Properties Type")]
|
#[error("Wrong Properties Type")]
|
||||||
WrongProps,
|
WrongProps,
|
||||||
|
|
||||||
|
#[error("Base scope has not been mounted yet")]
|
||||||
|
NotMounted,
|
||||||
|
|
||||||
// #[error("Out of compute credits")]
|
// #[error("Out of compute credits")]
|
||||||
// OutOfCredits,
|
// OutOfCredits,
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,17 @@ impl EventTrigger {
|
|||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a new "start" event that boots up the virtual dom if it is paused
|
||||||
|
pub fn start_event() -> Self {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum VirtualEvent {
|
pub enum VirtualEvent {
|
||||||
// the event to drain the current lifecycle queue
|
// the event to drain the current lifecycle queue
|
||||||
// Used to initate the dom
|
// Used to initate the dom
|
||||||
InitiateEvent,
|
StartEvent,
|
||||||
|
|
||||||
// Real events
|
// Real events
|
||||||
ClipboardEvent,
|
ClipboardEvent,
|
||||||
|
|||||||
@@ -23,20 +23,19 @@ use std::{
|
|||||||
/// An integrated virtual node system that progresses events and diffs UI trees.
|
/// An integrated virtual node system that progresses events and diffs UI trees.
|
||||||
/// Differences are converted into patches which a renderer can use to draw the UI.
|
/// Differences are converted into patches which a renderer can use to draw the UI.
|
||||||
pub struct VirtualDom {
|
pub struct VirtualDom {
|
||||||
// pub struct VirtualDom<P: Properties> {
|
|
||||||
/// All mounted components are arena allocated to make additions, removals, and references easy to work with
|
/// All mounted components are arena allocated to make additions, removals, and references easy to work with
|
||||||
/// A generational arean is used to re-use slots of deleted scopes without having to resize the underlying arena.
|
/// A generational arean is used to re-use slots of deleted scopes without having to resize the underlying arena.
|
||||||
pub(crate) components: Arena<Scope>,
|
pub(crate) components: Arena<Scope>,
|
||||||
|
|
||||||
/// The index of the root component.
|
/// The index of the root component.
|
||||||
|
/// Will not be ready if the dom is fresh
|
||||||
base_scope: Index,
|
base_scope: Index,
|
||||||
|
|
||||||
event_queue: Rc<RefCell<VecDeque<LifecycleEvent>>>,
|
event_queue: Rc<RefCell<VecDeque<LifecycleEvent>>>,
|
||||||
|
|
||||||
// Mark the root props with P, even though they're held by the root component
|
// todo: encapsulate more state into this so we can better reuse it
|
||||||
// This is done so we don't have a "generic" vdom, making it easier to hold references to it, especially when the holders
|
diff_bump: Bump,
|
||||||
// don't care about the generic props type
|
|
||||||
// Most implementations that use the VirtualDom won't care about the root props anyways.
|
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
_root_prop_type: std::any::TypeId,
|
_root_prop_type: std::any::TypeId,
|
||||||
}
|
}
|
||||||
@@ -56,75 +55,28 @@ impl VirtualDom {
|
|||||||
/// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
|
/// This is useful when a component tree can be driven by external state (IE SSR) but it would be too expensive
|
||||||
/// to toss out the entire tree.
|
/// to toss out the entire tree.
|
||||||
pub fn new_with_props<P: 'static>(root: FC<P>, root_props: P) -> Self {
|
pub fn new_with_props<P: 'static>(root: FC<P>, root_props: P) -> Self {
|
||||||
// 1. Create the component arena
|
let mut components = Arena::new();
|
||||||
// 2. Create the base scope (can never be removed)
|
|
||||||
// 3. Create the lifecycle queue
|
|
||||||
// 4. Create the event queue
|
|
||||||
|
|
||||||
todo!();
|
let event_queue = Rc::new(RefCell::new(VecDeque::new()));
|
||||||
// Arena allocate all the components
|
|
||||||
// This should make it *really* easy to store references in events and such
|
|
||||||
// let mut components = Arena::new();
|
|
||||||
|
|
||||||
// Create a reference to the component in the arena
|
// Create a reference to the component in the arena
|
||||||
// let base_scope = components.insert(Scope::new(root, None));
|
// Note: we are essentially running the "Mount" lifecycle event manually while the vdom doesnt yet exist
|
||||||
|
// This puts the dom in a usable state on creation, rather than being potentially invalid
|
||||||
|
let base_scope = components.insert(Scope::new::<_, P>(root, root_props, None));
|
||||||
|
|
||||||
// // Create a new mount event with no root container
|
// evaluate the component, pushing any updates its generates into the lifecycle queue
|
||||||
// let first_event = LifecycleEvent::mount(base_scope, None, 0, root_props);
|
// todo!
|
||||||
|
|
||||||
// // Create an event queue with a mount for the base scope
|
let _root_prop_type = TypeId::of::<P>();
|
||||||
// let event_queue = Rc::new(RefCell::new(vec![first_event].into_iter().collect()));
|
let diff_bump = Bump::new();
|
||||||
|
|
||||||
// let _root_prop_type = TypeId::of::<P>();
|
Self {
|
||||||
|
components,
|
||||||
// Self {
|
base_scope,
|
||||||
// components,
|
event_queue,
|
||||||
// base_scope,
|
diff_bump,
|
||||||
// event_queue,
|
_root_prop_type,
|
||||||
// _root_prop_type,
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
|
|
||||||
/// With access to the virtual dom, schedule an update to the Root component's props
|
|
||||||
pub fn update_props<P: 'static>(&mut self, new_props: P) -> Result<()> {
|
|
||||||
// Ensure the props match
|
|
||||||
if TypeId::of::<P>() != self._root_prop_type {
|
|
||||||
return Err(Error::WrongProps);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.event_queue
|
|
||||||
.as_ref()
|
|
||||||
.borrow_mut()
|
|
||||||
.push_back(LifecycleEvent {
|
|
||||||
event_type: LifecycleType::PropsChanged {
|
|
||||||
props: Box::new(new_props),
|
|
||||||
},
|
|
||||||
component_index: self.base_scope,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Schedule a future update for a component from outside the vdom!
|
|
||||||
///
|
|
||||||
/// This lets services external to the virtual dom interact directly with the component and event system.
|
|
||||||
pub fn queue_update() {}
|
|
||||||
|
|
||||||
pub fn start(&mut self) -> Result<EditList> {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Pop an event off the event queue and process it
|
|
||||||
/// Update the root props, and progress
|
|
||||||
/// Takes a bump arena to allocate into, making the diff phase as fast as possible
|
|
||||||
pub fn progress(&mut self) -> Result<()> {
|
|
||||||
let event = self
|
|
||||||
.event_queue
|
|
||||||
.as_ref()
|
|
||||||
.borrow_mut()
|
|
||||||
.pop_front()
|
|
||||||
.ok_or(Error::NoEvent)?;
|
|
||||||
self.process_lifecycle(event)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered.
|
/// This method is the most sophisticated way of updating the virtual dom after an external event has been triggered.
|
||||||
@@ -136,14 +88,22 @@ impl VirtualDom {
|
|||||||
/// If implementing an external renderer, this is the perfect method to combine with an async event loop that waits on
|
/// If implementing an external renderer, this is the perfect method to combine with an async event loop that waits on
|
||||||
/// listeners.
|
/// listeners.
|
||||||
///
|
///
|
||||||
|
/// Note: this method is not async and does not provide suspense-like functionality. It is up to the renderer to provide the
|
||||||
|
/// executor and handlers for suspense as show in the example.
|
||||||
|
///
|
||||||
/// ```ignore
|
/// ```ignore
|
||||||
|
/// let (sender, receiver) = channel::new();
|
||||||
|
/// sender.send(EventTrigger::start());
|
||||||
///
|
///
|
||||||
|
/// let mut dom = VirtualDom::new();
|
||||||
|
/// dom.suspense_handler(|event| sender.send(event));
|
||||||
///
|
///
|
||||||
///
|
/// while let Ok(diffs) = dom.progress_with_event(receiver.recv().await) {
|
||||||
|
/// render(diffs);
|
||||||
|
/// }
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
pub async fn progress_with_event(&mut self, evt: EventTrigger) -> Result<()> {
|
pub fn progress_with_event(&mut self, evt: EventTrigger) -> Result<EditList<'_>> {
|
||||||
// pub async fn progress_with_event(&mut self, evt: EventTrigger) -> Result<EditList<'_>> {
|
|
||||||
let EventTrigger {
|
let EventTrigger {
|
||||||
component_id,
|
component_id,
|
||||||
listener_id,
|
listener_id,
|
||||||
@@ -173,124 +133,93 @@ impl VirtualDom {
|
|||||||
// *then* run the non-prop updates that were not already covered by props
|
// *then* run the non-prop updates that were not already covered by props
|
||||||
|
|
||||||
let mut affected_components = Vec::new();
|
let mut affected_components = Vec::new();
|
||||||
|
|
||||||
// It's essentially draining the vec, but with some dancing to release the RefMut
|
// It's essentially draining the vec, but with some dancing to release the RefMut
|
||||||
// We also want to be able to push events into the queue from processing the event
|
// We also want to be able to push events into the queue from processing the event
|
||||||
while let Some(event) = {
|
while let Some(event) = {
|
||||||
let new_evt = self.event_queue.as_ref().borrow_mut().pop_front();
|
let new_evt = self.event_queue.as_ref().borrow_mut().pop_front();
|
||||||
new_evt
|
new_evt
|
||||||
} {
|
} {
|
||||||
affected_components.push(event.component_index);
|
if let Some(component_idx) = event.index() {
|
||||||
|
affected_components.push(component_idx);
|
||||||
|
}
|
||||||
self.process_lifecycle(event)?;
|
self.process_lifecycle(event)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let diff_bump = Bump::new();
|
// Reset and then build a new diff machine
|
||||||
let diff_machine = DiffMachine::new(&diff_bump);
|
// The previous edit list cannot be around while &mut is held
|
||||||
|
// Make sure variance doesnt break this
|
||||||
|
self.diff_bump.reset();
|
||||||
|
let diff_machine = DiffMachine::new(&self.diff_bump);
|
||||||
|
|
||||||
|
Ok(diff_machine.consume())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Using mutable access to the Virtual Dom, progress a given lifecycle event
|
||||||
|
fn process_lifecycle(&mut self, LifecycleEvent { event_type }: LifecycleEvent) -> Result<()> {
|
||||||
|
match event_type {
|
||||||
|
// Component needs to be mounted to the virtual dom
|
||||||
|
LifecycleType::Mount { to, under, props } => {}
|
||||||
|
|
||||||
|
// The parent for this component generated new props and the component needs update
|
||||||
|
LifecycleType::PropsChanged { props, component } => {}
|
||||||
|
|
||||||
|
// Component was messaged via the internal subscription service
|
||||||
|
LifecycleType::Callback { component } => {}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Using mutable access to the Virtual Dom, progress a given lifecycle event
|
/// With access to the virtual dom, schedule an update to the Root component's props.
|
||||||
///
|
/// This generates the appropriate Lifecycle even. It's up to the renderer to actually feed this lifecycle event
|
||||||
///
|
/// back into the event system to get an edit list.
|
||||||
///
|
pub fn update_props<P: 'static>(&mut self, new_props: P) -> Result<LifecycleEvent> {
|
||||||
///
|
// Ensure the props match
|
||||||
///
|
if TypeId::of::<P>() != self._root_prop_type {
|
||||||
///
|
return Err(Error::WrongProps);
|
||||||
fn process_lifecycle(
|
|
||||||
&mut self,
|
|
||||||
LifecycleEvent {
|
|
||||||
component_index: index,
|
|
||||||
event_type,
|
|
||||||
}: LifecycleEvent,
|
|
||||||
) -> Result<()> {
|
|
||||||
let scope = self.components.get_mut(index).ok_or(Error::NoEvent)?;
|
|
||||||
|
|
||||||
match event_type {
|
|
||||||
// Component needs to be mounted to the virtual dom
|
|
||||||
LifecycleType::Mount { to, under, props } => {
|
|
||||||
if let Some(other) = to {
|
|
||||||
// mount to another component
|
|
||||||
} else {
|
|
||||||
// mount to the root
|
|
||||||
}
|
|
||||||
|
|
||||||
// let g = props.as_ref();
|
|
||||||
// scope.run(g);
|
|
||||||
// scope.run(runner, props, dom);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The parent for this component generated new props and the component needs update
|
|
||||||
LifecycleType::PropsChanged { props } => {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component was successfully mounted to the dom
|
|
||||||
LifecycleType::Mounted {} => {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component was removed from the DOM
|
|
||||||
// Run any destructors and cleanup for the hooks and the dump the component
|
|
||||||
LifecycleType::Removed {} => {
|
|
||||||
let f = self.components.remove(index);
|
|
||||||
// let f = dom.components.remove(index);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component was messaged via the internal subscription service
|
|
||||||
LifecycleType::Messaged => {
|
|
||||||
//
|
|
||||||
}
|
|
||||||
|
|
||||||
// Event from renderer was fired with a given listener ID
|
|
||||||
//
|
|
||||||
LifecycleType::Callback { listener_id } => {}
|
|
||||||
|
|
||||||
// Run any post-render callbacks on a component
|
|
||||||
LifecycleType::Rendered => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(LifecycleEvent {
|
||||||
|
event_type: LifecycleType::PropsChanged {
|
||||||
|
props: Box::new(new_props),
|
||||||
|
component: self.base_scope,
|
||||||
|
},
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct LifecycleEvent {
|
pub struct LifecycleEvent {
|
||||||
pub component_index: Index,
|
|
||||||
pub event_type: LifecycleType,
|
pub event_type: LifecycleType,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The internal lifecycle event system is managed by these
|
|
||||||
/// Right now, we box the properties and but them in the enum
|
|
||||||
/// Later, we could directly call the chain of children without boxing
|
|
||||||
/// We could try to reuse the boxes somehow
|
|
||||||
pub enum LifecycleType {
|
pub enum LifecycleType {
|
||||||
|
// Component needs to be mounted, but its scope doesn't exist yet
|
||||||
Mount {
|
Mount {
|
||||||
to: Option<Index>,
|
to: Index,
|
||||||
under: usize,
|
under: usize,
|
||||||
props: Box<dyn std::any::Any>,
|
props: Box<dyn std::any::Any>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Parent was evalauted causing new props to generate
|
||||||
PropsChanged {
|
PropsChanged {
|
||||||
props: Box<dyn std::any::Any>,
|
props: Box<dyn std::any::Any>,
|
||||||
|
component: Index,
|
||||||
},
|
},
|
||||||
Rendered,
|
|
||||||
Mounted,
|
// Hook for the subscription API
|
||||||
Removed,
|
|
||||||
Messaged,
|
|
||||||
Callback {
|
Callback {
|
||||||
listener_id: i32,
|
component: Index,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LifecycleEvent {
|
impl LifecycleEvent {
|
||||||
// helper method for shortcutting to the enum type
|
fn index(&self) -> Option<Index> {
|
||||||
// probably not necessary
|
match &self.event_type {
|
||||||
fn mount<P: 'static>(which: Index, to: Option<Index>, under: usize, props: P) -> Self {
|
LifecycleType::Mount { to, under, props } => None,
|
||||||
Self {
|
|
||||||
component_index: which,
|
LifecycleType::PropsChanged { component, .. }
|
||||||
event_type: LifecycleType::Mount {
|
| LifecycleType::Callback { component } => Some(component.clone()),
|
||||||
to,
|
|
||||||
under,
|
|
||||||
props: Box::new(props),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,13 +2,20 @@
|
|||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
//!
|
//!
|
||||||
use dioxus_core::prelude::bumpalo::Bump;
|
|
||||||
use dioxus_core::prelude::*;
|
use dioxus_core::prelude::*;
|
||||||
use dioxus_web::*;
|
use dioxus_web::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
|
// todo: wire up the component macro macro
|
||||||
|
// div! { class = "asd" onclick = move |_| {}
|
||||||
|
// div! { class = "asd" onclick = move |_| {} }
|
||||||
|
// h1! { "123456" "123456" "123456" }
|
||||||
|
// h2! { "123456" "123456" "123456" }
|
||||||
|
// h3! { "123456" "123456" "123456" }
|
||||||
|
// };
|
||||||
|
|
||||||
WebsysRenderer::simple_render(html! {
|
WebsysRenderer::simple_render(html! {
|
||||||
<div>
|
<div>
|
||||||
<div class="flex items-center justify-center flex-col">
|
<div class="flex items-center justify-center flex-col">
|
||||||
|
|||||||
+52
-26
@@ -26,7 +26,10 @@ use dioxus_core::{
|
|||||||
events::EventTrigger,
|
events::EventTrigger,
|
||||||
prelude::{bumpalo::Bump, html, DiffMachine, VNode, VirtualDom, FC},
|
prelude::{bumpalo::Bump, html, DiffMachine, VNode, VirtualDom, FC},
|
||||||
};
|
};
|
||||||
use futures::{channel::mpsc, future, SinkExt, StreamExt};
|
use futures::{
|
||||||
|
channel::mpsc::{self, Sender},
|
||||||
|
future, SinkExt, StreamExt,
|
||||||
|
};
|
||||||
use mpsc::UnboundedSender;
|
use mpsc::UnboundedSender;
|
||||||
pub mod interpreter;
|
pub mod interpreter;
|
||||||
use interpreter::PatchMachine;
|
use interpreter::PatchMachine;
|
||||||
@@ -36,7 +39,6 @@ use interpreter::PatchMachine;
|
|||||||
///
|
///
|
||||||
pub struct WebsysRenderer {
|
pub struct WebsysRenderer {
|
||||||
internal_dom: VirtualDom,
|
internal_dom: VirtualDom,
|
||||||
// Map of handlers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WebsysRenderer {
|
impl WebsysRenderer {
|
||||||
@@ -63,31 +65,17 @@ impl WebsysRenderer {
|
|||||||
|
|
||||||
/// Run the renderer, progressing any events that crop up
|
/// Run the renderer, progressing any events that crop up
|
||||||
/// Yield on event handlers
|
/// Yield on event handlers
|
||||||
/// If the dom errors out, self is consumed and the dom is torn down
|
pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
|
||||||
pub async fn run(self) -> dioxus_core::error::Result<()> {
|
// Connect the listeners to the event loop
|
||||||
let WebsysRenderer { mut internal_dom } = self;
|
let (mut sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
|
||||||
|
|
||||||
// Progress the mount of the root component
|
// Send the start event
|
||||||
internal_dom
|
sender
|
||||||
.progress()
|
.send(EventTrigger::start_event())
|
||||||
.expect("Progressing the root failed :(");
|
.await
|
||||||
|
.expect("Should not fail");
|
||||||
|
|
||||||
// set up the channels to connect listeners to the event loop
|
prepare_websys_dom();
|
||||||
let (sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
|
|
||||||
|
|
||||||
// Iterate through the nodes, attaching the closure and sender to the listener
|
|
||||||
// {
|
|
||||||
// let mut remote_sender = sender.clone();
|
|
||||||
// let listener = move || {
|
|
||||||
// let event = EventTrigger::new();
|
|
||||||
// wasm_bindgen_futures::spawn_local(async move {
|
|
||||||
// remote_sender
|
|
||||||
// .send(event)
|
|
||||||
// .await
|
|
||||||
// .expect("Updating receiver failed");
|
|
||||||
// })
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Event loop waits for the receiver to finish up
|
// Event loop waits for the receiver to finish up
|
||||||
// TODO! Connect the sender to the virtual dom's suspense system
|
// TODO! Connect the sender to the virtual dom's suspense system
|
||||||
@@ -97,7 +85,7 @@ impl WebsysRenderer {
|
|||||||
// relevant listeners are ran
|
// relevant listeners are ran
|
||||||
// internal state is modified, components are tagged for changes
|
// internal state is modified, components are tagged for changes
|
||||||
|
|
||||||
match internal_dom.progress_with_event(event).await {
|
match self.internal_dom.progress_with_event(event) {
|
||||||
Err(_) => {}
|
Err(_) => {}
|
||||||
Ok(_) => {} // Ok(_) => render_diffs(),
|
Ok(_) => {} // Ok(_) => render_diffs(),
|
||||||
}
|
}
|
||||||
@@ -174,6 +162,44 @@ impl WebsysRenderer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn prepare_websys_dom() {
|
||||||
|
// Initialize the container on the dom
|
||||||
|
// Hook up the body as the root component to render tinto
|
||||||
|
let window = web_sys::window().expect("should have access to the Window");
|
||||||
|
let document = window
|
||||||
|
.document()
|
||||||
|
.expect("should have access to the Document");
|
||||||
|
let body = document.body().unwrap();
|
||||||
|
|
||||||
|
// Build a dummy div
|
||||||
|
let container: &Element = body.as_ref();
|
||||||
|
container.set_inner_html("");
|
||||||
|
container
|
||||||
|
.append_child(
|
||||||
|
document
|
||||||
|
.create_element("div")
|
||||||
|
.expect("should create element OK")
|
||||||
|
.as_ref(),
|
||||||
|
)
|
||||||
|
.expect("should append child OK");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Progress the mount of the root component
|
||||||
|
|
||||||
|
// Iterate through the nodes, attaching the closure and sender to the listener
|
||||||
|
// {
|
||||||
|
// let mut remote_sender = sender.clone();
|
||||||
|
// let listener = move || {
|
||||||
|
// let event = EventTrigger::new();
|
||||||
|
// wasm_bindgen_futures::spawn_local(async move {
|
||||||
|
// remote_sender
|
||||||
|
// .send(event)
|
||||||
|
// .await
|
||||||
|
// .expect("Updating receiver failed");
|
||||||
|
// })
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::env;
|
use std::env;
|
||||||
|
|||||||
Reference in new issue
Block a user