1
0
Эх сурвалжийг харах

Feat: a few bugs, but the event system works!

Jonathan Kelley 4 жил өмнө
parent
commit
3b30fa6

+ 3 - 3
packages/core/src/changelist.rs

@@ -64,9 +64,9 @@ pub enum Edit<'d> {
 /// We can go back and forth between the two via methods on GI
 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
 pub struct CbIdx {
-    gi_id: usize,
-    gi_gen: u64,
-    listener_idx: usize,
+    pub gi_id: usize,
+    pub gi_gen: u64,
+    pub listener_idx: usize,
 }
 
 impl CbIdx {

+ 1 - 0
packages/core/src/context.rs

@@ -102,6 +102,7 @@ pub mod hooks {
 
     use super::*;
 
+    #[derive(Debug)]
     pub struct Hook(pub Box<dyn std::any::Any>);
 
     impl Hook {

+ 5 - 4
packages/core/src/dodriodiff.rs

@@ -68,7 +68,7 @@ pub struct DiffMachine<'a> {
 
 impl<'a> DiffMachine<'a> {
     pub fn new(bump: &'a Bump) -> Self {
-        log::debug!("starting diff machine");
+        // log::debug!("starting diff machine");
         Self {
             change_list: EditMachine::new(bump),
             immediate_queue: Vec::new(),
@@ -88,7 +88,8 @@ impl<'a> DiffMachine<'a> {
         new: &VNode<'a>,
         scope: Option<generational_arena::Index>,
     ) {
-        log::debug!("Diffing nodes");
+        log::debug!("old {:#?}", old);
+        log::debug!("new {:#?}", new);
 
         // Set it while diffing
         // Reset it when finished diffing
@@ -131,7 +132,7 @@ impl<'a> DiffMachine<'a> {
             // compare elements
             // if different, schedule different types of update
             (VNode::Element(eold), VNode::Element(enew)) => {
-                log::debug!("elements are different");
+                // log::debug!("elements are different");
                 // If the element type is completely different, the element needs to be re-rendered completely
                 if enew.tag_name != eold.tag_name || enew.namespace != eold.namespace {
                     self.change_list.commit_traversal();
@@ -894,7 +895,7 @@ impl<'a> DiffMachine<'a> {
                 children,
                 namespace,
             }) => {
-                log::info!("Creating {:#?}", node);
+                // log::info!("Creating {:#?}", node);
                 if let Some(namespace) = namespace {
                     self.change_list.create_element_ns(tag_name, namespace);
                 } else {

+ 16 - 3
packages/core/src/events.rs

@@ -6,16 +6,29 @@
 //! The goal here is to provide a consistent event interface across all renderer types
 use generational_arena::Index;
 
+use crate::innerlude::CbIdx;
+
 #[derive(Debug)]
 pub struct EventTrigger {
     pub component_id: Index,
-    pub listener_id: u32,
+    pub listener_id: usize,
     pub event: VirtualEvent,
 }
 
 impl EventTrigger {
-    pub fn new() -> Self {
-        todo!()
+    pub fn new(event: VirtualEvent, cb: CbIdx) -> Self {
+        let CbIdx {
+            gi_id,
+            gi_gen,
+            listener_idx,
+        } = cb;
+
+        let component_id = Index::from_raw_parts(gi_id, gi_gen);
+        Self {
+            component_id,
+            listener_id: listener_idx,
+            event,
+        }
     }
 }
 

+ 1 - 0
packages/core/src/hooks.rs

@@ -51,6 +51,7 @@ mod use_state_def {
                 caller: Box::new(|_| println!("setter called!")),
             },
             move |hook| {
+                log::debug!("Use_state set called");
                 let inner = hook.new_val.clone();
                 let scheduled_update = ctx.schedule_update();
 

+ 3 - 2
packages/core/src/nodebuilder.rs

@@ -3,6 +3,7 @@
 use std::ops::Deref;
 
 use crate::{
+    events::VirtualEvent,
     innerlude::VComponent,
     nodes::{Attribute, Listener, NodeKey, VNode},
     prelude::VElement,
@@ -338,7 +339,7 @@ where
     ///     .finish();
     /// ```
     #[inline]
-    pub fn on(mut self, event: &'static str, callback: impl Fn(()) + 'a) -> Self {
+    pub fn on(mut self, event: &'static str, callback: impl Fn(VirtualEvent) + 'a) -> Self {
         self.listeners.push(Listener {
             event,
             callback: self.bump.alloc(callback),
@@ -1081,7 +1082,7 @@ pub fn on<'a, 'b>(
     // pub fn on<'a, 'b, F: 'static>(
     bump: &'a Bump,
     event: &'static str,
-    callback: impl Fn(()) + 'a,
+    callback: impl Fn(VirtualEvent) + 'a,
 ) -> Listener<'a> {
     Listener {
         event,

+ 4 - 2
packages/core/src/nodes.rs

@@ -94,6 +94,8 @@ mod vnode {
 }
 
 mod velement {
+    use crate::events::VirtualEvent;
+
     use super::*;
     use std::fmt::Debug;
 
@@ -188,7 +190,7 @@ mod velement {
         // pub(crate) _i: &'bump str,
         // #[serde(skip_serializing, skip_deserializing, default="")]
         // /// The callback to invoke when the event happens.
-        pub(crate) callback: &'bump (dyn Fn(())),
+        pub(crate) callback: &'bump (dyn Fn(VirtualEvent)),
     }
     impl Debug for Listener<'_> {
         fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -197,7 +199,7 @@ mod velement {
                 .finish()
         }
     }
-    pub(crate) type ListenerCallback<'a> = &'a (dyn Fn(()));
+    pub(crate) type ListenerCallback<'a> = &'a (dyn Fn(VirtualEvent));
     union CallbackFatPtr<'a> {
         callback: ListenerCallback<'a>,
         parts: (u32, u32),

+ 50 - 11
packages/core/src/scope.rs

@@ -36,8 +36,10 @@ pub struct Scope {
 
     pub frames: ActiveFrame,
 
-    // IE Which listeners need to be woken up?
-    pub listeners: Vec<Box<dyn Fn(crate::events::VirtualEvent)>>,
+    // List of listeners generated when CTX is evaluated
+    pub listeners: Vec<*const dyn Fn(crate::events::VirtualEvent)>,
+    // pub listeners: Vec<*const dyn Fn(crate::events::VirtualEvent)>,
+    // pub listeners: Vec<Box<dyn Fn(crate::events::VirtualEvent)>>,
 
     // lying, cheating reference >:(
     pub props: Box<dyn std::any::Any>,
@@ -55,9 +57,8 @@ impl Scope {
         // Capture the caller
         let caller = f as *const ();
 
-        let listeners: Vec<Box<dyn Fn(crate::events::VirtualEvent)>> = vec![Box::new(|_| {
-            log::info!("Base listener called");
-        })];
+        let listeners = vec![];
+        // let listeners: Vec<Box<dyn Fn(crate::events::VirtualEvent)>> = vec![];
 
         let old_frame = BumpFrame {
             bump: Bump::new(),
@@ -95,6 +96,7 @@ impl Scope {
         let frame = {
             let frame = self.frames.next();
             frame.bump.reset();
+            log::debug!("Rednering into frame {:?}", frame as *const _);
             frame
         };
 
@@ -145,6 +147,20 @@ impl Scope {
                 .borrow_mut()
                 .take()
                 .expect("Viewing did not happen");
+
+            // todo:
+            // make this so we dont have to iterate through the vnodes to get its listener
+            let mut listeners = vec![];
+            retrieve_listeners(&frame.head_node, &mut listeners);
+            self.listeners = listeners
+                .into_iter()
+                .map(|f| {
+                    let g = f.callback;
+                    g as *const _
+                })
+                .collect();
+
+            // consume the listeners from the head_node into a list of boxed ref listeners
         }
     }
 
@@ -159,6 +175,18 @@ impl Scope {
     }
 }
 
+fn retrieve_listeners(node: &VNode<'static>, listeners: &mut Vec<&Listener>) {
+    if let VNode::Element(el) = *node {
+        for listener in el.listeners {
+            // let g = listener as *const Listener;
+            listeners.push(listener);
+        }
+        for child in el.children {
+            retrieve_listeners(child, listeners);
+        }
+    }
+}
+
 // todo, do better with the active frame stuff
 // somehow build this vnode with a lifetime tied to self
 // This root node has  "static" lifetime, but it's really not static.
@@ -184,7 +212,7 @@ impl ActiveFrame {
     }
 
     fn current_head_node<'b>(&'b self) -> &'b VNode<'b> {
-        let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 != 0 {
+        let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 == 0 {
             true => &self.frames[0],
             false => &self.frames[1],
         };
@@ -196,7 +224,7 @@ impl ActiveFrame {
     }
 
     fn prev_head_node<'b>(&'b self) -> &'b VNode<'b> {
-        let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 == 0 {
+        let raw_node = match self.idx.borrow().load(Ordering::Relaxed) & 1 != 0 {
             true => &self.frames[0],
             false => &self.frames[1],
         };
@@ -211,11 +239,22 @@ impl ActiveFrame {
     fn next(&mut self) -> &mut BumpFrame {
         self.idx.fetch_add(1, Ordering::Relaxed);
         let cur = self.idx.borrow().load(Ordering::Relaxed);
-        match cur % 1 {
-            1 => &mut self.frames[1],
-            0 => &mut self.frames[0],
-            _ => unreachable!("mod cannot by non-zero"),
+        log::debug!("Next frame! {}", cur);
+
+        if cur % 2 == 0 {
+            log::debug!("Chosing frame 0");
+            &mut self.frames[0]
+        } else {
+            log::debug!("Chosing frame 1");
+            &mut self.frames[1]
         }
+        // match cur % 1 {
+        //     0 => {
+        //     }
+        //     1 => {
+        //     }
+        //     _ => unreachable!("mod cannot by non-zero"),
+        // }
     }
 }
 

+ 48 - 14
packages/core/src/virtual_dom.rs

@@ -140,29 +140,63 @@ impl VirtualDom {
 
         let component = self
             .components
-            .get(component_id)
+            .get_mut(component_id)
             .expect("Component should exist if an event was triggered");
 
-        let listener = component
-            .listeners
-            .get(listener_id as usize)
-            .expect("Listener should exist if it was triggered")
-            .as_ref();
+        log::debug!("list: {}", component.listeners.len());
+
+        let listener = unsafe {
+            component
+                .listeners
+                .get(listener_id as usize)
+                .expect("Listener should exist if it was triggered")
+                .as_ref()
+        }
+        .unwrap();
 
         // Run the callback with the user event
         listener(source);
 
+        // Reset and then build a new diff machine
+        // The previous edit list cannot be around while &mut is held
+        // Make sure variance doesnt break this
+        self.diff_bump.reset();
+        let mut diff_machine = DiffMachine::new(&self.diff_bump);
+
+        // this is still a WIP
+        // we'll need to re-fecth all the scopes that were changed and build the diff machine
+        // fetch the component again
+        // let component = self
+        //     .components
+        //     .get_mut(self.base_scope)
+        //     .expect("Root should always exist");
+
+        component.run::<()>();
+
+        diff_machine.diff_node(
+            component.old_frame(),
+            component.new_frame(),
+            Some(self.base_scope),
+        );
+        // diff_machine.diff_node(
+        //     component.old_frame(),
+        //     component.new_frame(),
+        //     Some(self.base_scope),
+        // );
+
+        Ok(diff_machine.consume())
+        // Err(crate::error::Error::NoEvent)
         // Mark dirty components. Descend from the highest node until all dirty nodes are updated.
-        let mut affected_components = Vec::new();
+        // let mut affected_components = Vec::new();
 
-        while let Some(event) = self.pop_event() {
-            if let Some(component_idx) = event.index() {
-                affected_components.push(component_idx);
-            }
-            self.process_lifecycle(event)?;
-        }
+        // while let Some(event) = self.pop_event() {
+        //     if let Some(component_idx) = event.index() {
+        //         affected_components.push(component_idx);
+        //     }
+        //     self.process_lifecycle(event)?;
+        // }
 
-        todo!()
+        // todo!()
     }
 
     /// Using mutable access to the Virtual Dom, progress a given lifecycle event

+ 0 - 4
packages/web/examples/basic.rs

@@ -6,10 +6,6 @@ use dioxus_web::*;
 fn main() {
     // Setup logging
     wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
-    // wasm_logger::init(wasm_logger::Config::with_prefix(
-    //     log::Level::Debug,
-    //     "dioxus_core",
-    // ));
     console_error_panic_hook::set_once();
 
     // Run the app

+ 52 - 11
packages/web/examples/jackjill.rs

@@ -1,13 +1,10 @@
-use dioxus::prelude::bumpalo;
-use dioxus::prelude::format_args_f;
 use dioxus::prelude::*;
 use dioxus_core as dioxus;
-use dioxus_core::prelude::html;
 use dioxus_web::WebsysRenderer;
 
 fn main() {
-    pretty_env_logger::init();
-    log::info!("Hello!");
+    wasm_logger::init(wasm_logger::Config::new(log::Level::Debug));
+    console_error_panic_hook::set_once();
 
     wasm_bindgen_futures::spawn_local(WebsysRenderer::start(Example))
 }
@@ -15,11 +12,55 @@ fn main() {
 static Example: FC<()> = |ctx, props| {
     let (name, set_name) = use_state(&ctx, || "...?");
 
+    log::debug!("Running component....");
+
     ctx.view(html! {
-        <div>
-            <h1> "Hello, {name}" </h1>
-            <button onclick={move |_| set_name("jack")}> "jack!" </button>
-            <button onclick={move |_| set_name("jill")}> "jill!" </button>
-        </div>
-    })
+    // <div>
+    //     <h1> "Hello, {name}" </h1>
+    //     <button onclick={move |_| set_name("jack")}> "jack!" </button>
+    //     <button
+    //         onclick={move |_| set_name("jill")}
+    //         onclick={move |_| set_name("jill")}
+    //     > "jill!" </button>
+    // </div>
+            <div>
+                <section class="py-12 px-4 text-center">
+                    <div class="w-full max-w-2xl mx-auto">
+                        // Tagline
+                        <span class="text-sm font-semibold">
+                            "Dioxus Example: Jack and Jill"
+                        </span>
+
+                        // Header
+                        <h2 class="text-5xl mt-2 mb-6 leading-tight font-semibold font-heading">
+                            "Hello, {name}"
+                        </h2>
+
+                        // Control buttons
+                        <div>
+                            <button
+                                class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-700 font-semibold rounded shadow"
+                                // onclick={move |_| log::debug!("set jack")}>
+                                onclick={move |_| set_name("jack")}>
+                                "Jack!"
+                                </button>
+
+                                <button
+                                class="inline-block py-4 px-8 mr-6 leading-none text-white bg-indigo-600 hover:bg-indigo-700 font-semibold rounded shadow"
+                                // onclick={move |_| log::debug!("set jill")}>
+                                onclick={move |_| set_name("jill")}
+                                onclick={move |_| set_name("jill")}>
+                                "Jill!"
+                            </button>
+                        </div>
+                    </div>
+                </section>
+            </div>
+        })
 };
+
+// <div>
+//     <h1> "Hello, {name}" </h1>
+//     <button onclick={move |_| set_name("jack   .")}> "jack!" </button>
+//     <button onclick={move |_| set_name("jill   .")}> "jill!" </button>
+// </div>

+ 92 - 53
packages/web/src/interpreter.rs

@@ -1,6 +1,9 @@
-use std::fmt::Debug;
+use std::{borrow::Borrow, fmt::Debug, sync::Arc};
 
-use dioxus_core::{changelist::Edit, events::EventTrigger};
+use dioxus_core::{
+    changelist::{CbIdx, Edit},
+    events::{EventTrigger, MouseEvent, VirtualEvent},
+};
 use fxhash::FxHashMap;
 use log::debug;
 use wasm_bindgen::{closure::Closure, JsCast};
@@ -9,10 +12,13 @@ use web_sys::{window, Document, Element, Event, HtmlInputElement, HtmlOptionElem
 #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)]
 pub struct CacheId(u32);
 
-struct RootCallback(Box<dyn Fn(EventTrigger)>);
+#[derive(Clone)]
+struct RootCallback(Arc<dyn Fn(EventTrigger)>);
 impl std::fmt::Debug for RootCallback {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        todo!()
+        Ok(())
+        // a no-op for now
+        // todo!()
     }
 }
 
@@ -37,15 +43,18 @@ pub(crate) struct EventDelegater {
     callback_id: usize,
 
     // map of listener types to number of those listeners
-    listeners: FxHashMap<&'static str, (usize, Closure<dyn Fn()>)>,
+    listeners: FxHashMap<String, (usize, Closure<dyn FnMut(&Event)>)>,
 
     // Map of callback_id to component index and listener id
     callback_map: FxHashMap<usize, (usize, usize)>,
+
+    trigger: RootCallback,
 }
 
 impl EventDelegater {
-    pub fn new(root: Element) -> Self {
+    pub fn new(root: Element, trigger: impl Fn(EventTrigger) + 'static) -> Self {
         Self {
+            trigger: RootCallback(Arc::new(trigger)),
             root,
             callback_id: 0,
             listeners: FxHashMap::default(),
@@ -53,12 +62,55 @@ impl EventDelegater {
         }
     }
 
-    pub fn add_listener(
-        &mut self,
-        event: &'static str,
-        gi: generational_arena::Index,
-        listener_id: usize,
-    ) {
+    pub fn add_listener(&mut self, event: &str, cb: CbIdx) {
+        if let Some(entry) = self.listeners.get_mut(event) {
+            entry.0 += 1;
+        } else {
+            let trigger = self.trigger.clone();
+            let handler = Closure::wrap(Box::new(move |event: &web_sys::Event| {
+                log::debug!("Handling event!");
+
+                let target = event
+                    .target()
+                    .expect("missing target")
+                    .dyn_into::<Element>()
+                    .expect("not a valid element");
+
+                let typ = event.type_();
+
+                let gi_id: usize = target
+                    .get_attribute(&format!("dioxus-giid-{}", typ))
+                    .and_then(|v| v.parse().ok())
+                    .unwrap_or_default();
+
+                let gi_gen: u64 = target
+                    .get_attribute(&format!("dioxus-gigen-{}", typ))
+                    .and_then(|v| v.parse().ok())
+                    .unwrap_or_default();
+
+                let li_idx: usize = target
+                    .get_attribute(&format!("dioxus-lidx-{}", typ))
+                    .and_then(|v| v.parse().ok())
+                    .unwrap_or_default();
+
+                // Call the trigger
+                trigger.0.as_ref()(EventTrigger::new(
+                    virtual_event_from_websys_event(event),
+                    CbIdx {
+                        gi_gen,
+                        gi_id,
+                        listener_idx: li_idx,
+                    },
+                ));
+            }) as Box<dyn FnMut(&Event)>);
+
+            self.root
+                .add_event_listener_with_callback(event, (&handler).as_ref().unchecked_ref())
+                .unwrap();
+
+            // Increment the listeners
+            self.listeners.insert(event.into(), (1, handler));
+        }
     }
 }
 
@@ -106,14 +158,14 @@ impl Stack {
 }
 
 impl PatchMachine {
-    pub fn new(root: Element, event_callback: impl Fn(EventTrigger)) -> Self {
+    pub fn new(root: Element, event_callback: impl Fn(EventTrigger) + 'static) -> Self {
         let document = window()
             .expect("must have access to the window")
             .document()
             .expect("must have access to the Document");
 
         // attach all listeners to the container element
-        let events = EventDelegater::new(root.clone());
+        let events = EventDelegater::new(root.clone(), event_callback);
 
         Self {
             root,
@@ -146,31 +198,6 @@ impl PatchMachine {
         // self.templates.get(&id)
     }
 
-    /// On any listener wakeup, find the listener that generated the event from th e attribute
-    // pub fn init_events_trampoline(&mut self, _trampoline: ()) {
-    pub fn init_events_trampoline(&mut self, event_channel: impl Fn(EventTrigger)) {
-        // self.callback = Some(Closure::wrap(Box::new(move |event: &web_sys::Event| {
-        //     let target = event
-        //         .target()
-        //         .expect("missing target")
-        //         .dyn_into::<Element>()
-        //         .expect("not a valid element");
-        //     let typ = event.type_();
-        //     let a: u32 = target
-        //         .get_attribute(&format!("dioxus-a-{}", typ))
-        //         .and_then(|v| v.parse().ok())
-        //         .unwrap_or_default();
-
-        //     let b: u32 = target
-        //         .get_attribute(&format!("dioxus-b-{}", typ))
-        //         .and_then(|v| v.parse().ok())
-        //         .unwrap_or_default();
-
-        //     // get a and b from the target
-        //     trampoline(event.clone(), a, b);
-        // }) as Box<dyn FnMut(&Event)>));
-    }
-
     pub fn handle_edit(&mut self, edit: &Edit) {
         match *edit {
             // 0
@@ -319,11 +346,7 @@ impl PatchMachine {
             }
 
             // 11
-            Edit::NewEventListener {
-                event_type,
-                idx: a,
-                b,
-            } => {
+            Edit::NewEventListener { event_type, idx } => {
                 // attach the correct attributes to the element
                 // these will be used by accessing the event's target
                 // This ensures we only ever have one handler attached to the root, but decide
@@ -341,24 +364,33 @@ impl PatchMachine {
                 // )
                 // .unwrap();
 
-                debug!("adding attributes: {}, {}", a, b);
-                el.set_attribute(&format!("dioxus-a-{}", event_type), &a.to_string())
+                // debug!("adding attributes: {}, {}", a, b);
+
+                let CbIdx {
+                    gi_id,
+                    gi_gen,
+                    listener_idx: lidx,
+                } = idx;
+
+                el.set_attribute(&format!("dioxus-giid-{}", event_type), &gi_id.to_string())
+                    .unwrap();
+                el.set_attribute(&format!("dioxus-gigen-{}", event_type), &gi_gen.to_string())
                     .unwrap();
-                el.set_attribute(&format!("dioxus-b-{}", event_type), &b.to_string())
+                el.set_attribute(&format!("dioxus-lidx-{}", event_type), &lidx.to_string())
                     .unwrap();
 
-                self.events.add_listener(event_type, gi, listener_id)
+                self.events.add_listener(event_type, idx);
             }
 
             // 12
-            Edit::UpdateEventListener { event_type, a, b } => {
+            Edit::UpdateEventListener { event_type, idx } => {
                 // update our internal mapping, and then modify the attribute
 
                 if let Some(el) = self.stack.top().dyn_ref::<Element>() {
-                    el.set_attribute(&format!("dioxus-a-{}", event_type), &a.to_string())
-                        .unwrap();
-                    el.set_attribute(&format!("dioxus-b-{}", event_type), &b.to_string())
-                        .unwrap();
+                    // el.set_attribute(&format!("dioxus-a-{}", event_type), &a.to_string())
+                    //     .unwrap();
+                    // el.set_attribute(&format!("dioxus-b-{}", event_type), &b.to_string())
+                    //     .unwrap();
                 }
             }
 
@@ -468,3 +500,10 @@ impl PatchMachine {
     //     // self.templates.contains_key(&id)
     // }
 }
+
+fn virtual_event_from_websys_event(event: &web_sys::Event) -> VirtualEvent {
+    match event.type_().as_str() {
+        "click" => VirtualEvent::MouseEvent(MouseEvent {}),
+        _ => VirtualEvent::OtherEvent,
+    }
+}

+ 33 - 10
packages/web/src/lib.rs

@@ -59,12 +59,19 @@ impl WebsysRenderer {
     }
 
     pub async fn run(&mut self) -> dioxus_core::error::Result<()> {
-        let (sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
+        let (mut sender, mut receiver) = mpsc::unbounded::<EventTrigger>();
 
         let body_element = prepare_websys_dom();
-        let mut patch_machine = interpreter::PatchMachine::new(body_element.clone(), |_| {});
+
+        let mut patch_machine = interpreter::PatchMachine::new(body_element.clone(), move |ev| {
+            log::debug!("Event trigger! {:#?}", ev);
+            let mut c = sender.clone();
+            wasm_bindgen_futures::spawn_local(async move {
+                c.send(ev).await.unwrap();
+            });
+        });
         let root_node = body_element.first_child().unwrap();
-        patch_machine.stack.push(root_node);
+        patch_machine.stack.push(root_node.clone());
 
         // todo: initialize the event registry properly on the root
 
@@ -72,17 +79,32 @@ impl WebsysRenderer {
             log::debug!("patching with  {:?}", edit);
             patch_machine.handle_edit(edit);
         });
+        log::debug!("patch stack size {:?}", patch_machine.stack);
 
         // Event loop waits for the receiver to finish up
         // TODO! Connect the sender to the virtual dom's suspense system
         // Suspense is basically an external event that can force renders to specific nodes
         while let Some(event) = receiver.next().await {
+            log::debug!("patch stack size before {:#?}", patch_machine.stack);
+            // patch_machine.reset();
+            // patch_machine.stack.push(root_node.clone());
             self.internal_dom
                 .progress_with_event(event)?
                 .iter()
                 .for_each(|edit| {
+                    log::debug!("edit stream {:?}", edit);
                     patch_machine.handle_edit(edit);
                 });
+
+            log::debug!("patch stack size after {:#?}", patch_machine.stack);
+            patch_machine.reset();
+            // our root node reference gets invalidated
+            // not sure why
+            // for now, just select the first child again.
+            // eventually, we'll just make our own root element instead of using body
+            // or just use body directly IDEK
+            let root_node = body_element.first_child().unwrap();
+            patch_machine.stack.push(root_node.clone());
         }
 
         Ok(()) // should actually never return from this, should be an error, rustc just cant see it
@@ -146,13 +168,14 @@ mod tests {
         log::info!("Hello!");
 
         wasm_bindgen_futures::spawn_local(WebsysRenderer::start(|ctx, _| {
-            ctx.view(html! {
-                <div>
-                    "Hello world"
-                    <button onclick={move |_| log::info!("button1 clicked!")}> "click me" </button>
-                    <button onclick={move |_| log::info!("button2 clicked!")}> "click me" </button>
-                </div>
-            })
+            todo!()
+            // ctx.view(html! {
+            //     <div>
+            //         "Hello world"
+            //         <button onclick={move |_| log::info!("button1 clicked!")}> "click me" </button>
+            //         <button onclick={move |_| log::info!("button2 clicked!")}> "click me" </button>
+            //     </div>
+            // })
         }))
     }
 }