Skip to content

Commit

Permalink
Added a (WIP) struct for keyed elements
Browse files Browse the repository at this point in the history
  • Loading branch information
David-OConnor committed Mar 3, 2019
1 parent 274d8c5 commit fd0174b
Show file tree
Hide file tree
Showing 4 changed files with 61 additions and 16 deletions.
30 changes: 29 additions & 1 deletion src/dom_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,13 @@ impl<Ms> UpdateEl<El<Ms>> for Tag {
}
}

impl<Ms> UpdateEl<El<Ms>> for Optimize {
// This, or some other mechanism seems to work for String too... note sure why.
fn update(self, el: &mut El<Ms>) {
el.optimizations.push(self)
}
}

/// Similar to tag population.
macro_rules! make_attrs {
// Create shortcut macros for any element; populate these functions in this module.
Expand Down Expand Up @@ -1019,6 +1026,13 @@ make_tags! {
Style => "style", View => "view"
}

/// WIP that marks elements in ways to improve diffing and rendering efficiency.
#[derive(Copy, Clone, Debug)]
pub enum Optimize {
Key(u32), // Helps correctly match children, prevening unecessary rerenders
Static, // unimplemented, and possibly unecessary
}

/// An component in our virtual DOM.
#[derive(Debug)] // todo: Custom debug implementation where children are on new lines and indented.
pub struct El<Ms: 'static> {
Expand Down Expand Up @@ -1052,6 +1066,7 @@ pub struct El<Ms: 'static> {
// Lifecycle hooks
pub hooks: LifecycleHooks,
pub empty: bool, // Indicates not to render anything.
optimizations: Vec<Optimize>
}

type HookFn = Box<FnMut(&web_sys::Node)>;
Expand Down Expand Up @@ -1102,7 +1117,8 @@ impl<Ms> El<Ms> {
// static: false,
// static_to_parent: false,
hooks: LifecycleHooks::default(),
empty: false
empty: false,
optimizations: Vec::new(),
}
}

Expand Down Expand Up @@ -1171,6 +1187,16 @@ impl<Ms> El<Ms> {
self.text = Some(text.into())
}

/// Shortcut for finding the key, if one exists
pub fn key(&self) -> Option<u32> {
for o in &self.optimizations {
if let Optimize::Key(key) = o {
return Some(*key)
}
}
None
}

/// Output the HTML of this node, including all its children, recursively.
fn _html(&self) -> String {
let text = self.text.clone().unwrap_or_default();
Expand Down Expand Up @@ -1209,6 +1235,7 @@ impl<Ms> El<Ms> {
// control: self.control,
hooks: LifecycleHooks::default(),
empty: false,
optimizations: Vec::new(),
}
}

Expand Down Expand Up @@ -1244,6 +1271,7 @@ impl<Ms> Clone for El<Ms> {
namespace: self.namespace.clone(),
hooks: LifecycleHooks::default(),
empty: self.empty,
optimizations: self.optimizations.clone(),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ pub mod prelude {
pub use crate::{
dom_types::{
did_mount, did_update, input_ev, keyboard_ev, mouse_ev, raw_ev, simple_ev,
will_unmount, At, El, Ev, Tag, UpdateEl,
will_unmount, At, El, Ev, Optimize::Key, Tag, UpdateEl,
},
shortcuts::*, // appears not to work.
// vdom::{Update, Update::Render, Update::Skip, Update::RenderThen},
Expand Down
27 changes: 13 additions & 14 deletions src/routing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,9 @@ fn get_search() -> String {
/// For setting up landing page routing. Unlike normal routing, we can't rely
/// on the popstate state, so must go off path, hash, and search directly.
pub fn initial<Ms, Mdl>(app: App<Ms, Mdl>, routes: fn(&Url) -> Ms) -> App<Ms, Mdl>
where
Ms: Clone + 'static,
Mdl: 'static,
where
Ms: Clone + 'static,
Mdl: 'static,
{
let raw_path = get_path();
let path_ref: Vec<&str> = raw_path.split('/').collect();
Expand Down Expand Up @@ -211,12 +211,10 @@ pub fn setup_popstate_listener<Ms, Mdl>(app: &App<Ms, Mdl>, routes: fn(&Url) ->
app.data.popstate_closure.replace(Some(closure));
}

/// Set up a listener that intercepts clicks on <a> and <button> tags, so we can prevent page reloads for
/// internal links. Run this on load.
/// Set up a listener that intercepts clicks on elements containing an Href attribute,
/// so we can prevent page refreshfor internal links, and route internally. Run this on load.
pub fn setup_link_listener<Ms: Clone, Mdl>(app: &App<Ms, Mdl>, routes: fn(&Url) -> Ms) {
// todo DRY with setup_popstate listener.
// todo Deal with excessive nesting.

let app_for_closure = app.clone();
let closure = Closure::wrap(
Box::new(move |event: web_sys::Event| {
Expand All @@ -231,16 +229,17 @@ pub fn setup_link_listener<Ms: Clone, Mdl>(app: &App<Ms, Mdl>, routes: fn(&Url)
if let Some(first) = href.chars().next() {
// The first character being / indicates a rel link, which is what
// we're intercepting.
if first == '/' {
event.prevent_default();
// Route internally based on href's value
let url = Url::new(href.split('/').collect());
app_for_closure.update(routes(&url));
push_route(url);
// todo: Handle other cases that imply a relative link.
if first != '/' {
return
}
event.prevent_default(); // Prevent page refresh
// Route internally based on href's value
let url = Url::new(href.split('/').collect());
app_for_closure.update(routes(&url));
push_route(url);
}
}

}
}
})
Expand Down
18 changes: 18 additions & 0 deletions src/vdom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -574,8 +574,26 @@ fn patch<Ms: Clone>(
let mut old_children_patched = Vec::new();

for (i_new, child_new) in new.children.iter_mut().enumerate() {

// If a key's specified, use it to match the child
// There can be multiple optomizations, but assume one key. If there are multiple
// keys, use the first (There should only be one, but no constraints atm).
if let Some(key) = child_new.key() {
let _matching = old.children.iter().filter(|c| c.key() == Some(key));
// todo continue implementation: Patch and re-order.
}


match old.children.get(i_new) {
Some(child_old) => {
// todo: This approach is still inefficient use of key, since it overwrites
// todo non-matching keys, preventing them from being found later.
if let Some(key) = child_new.key() {
if child_old.key() == Some(key) {
continue
}
}

// Don't compare equality here; we do that at the top of this function
// in the recursion.
patch(document, &mut child_old.clone(), child_new, &old_el_ws, &mailbox);
Expand Down

0 comments on commit fd0174b

Please sign in to comment.