From cf690387723bc9e9662c4a0bbf28a77134b02a75 Mon Sep 17 00:00:00 2001 From: Cecile Tonglet Date: Wed, 16 Sep 2020 18:24:02 +0200 Subject: [PATCH] Real performance improvement --- src/tree.rs | 173 +++++++++++++++++++++++++++++++++------------------- 1 file changed, 109 insertions(+), 64 deletions(-) diff --git a/src/tree.rs b/src/tree.rs index b0c5968..a379c10 100644 --- a/src/tree.rs +++ b/src/tree.rs @@ -4,6 +4,7 @@ use crate::Intent; pub use id_tree::*; use std::cell::{Ref, RefCell, RefMut}; use std::collections::hash_map::DefaultHasher; +use std::collections::HashMap; use std::hash::{Hash, Hasher}; use std::rc::Rc; use yew::prelude::*; @@ -42,6 +43,7 @@ impl From>> for TreeData { pub struct Tree { props: Props, + previous_expanded_state: RefCell>, } #[derive(Clone, PartialEq, Properties)] @@ -50,11 +52,11 @@ pub struct Props { pub is_expanded: bool, pub tree: TreeData, #[prop_or_default] - pub on_collapse: Option>, + pub on_collapse: Option>, #[prop_or_default] - pub on_expand: Option>, + pub on_expand: Option>, #[prop_or_default] - pub onclick: Option>, + pub onclick: Option>, } pub struct NodeData { @@ -109,7 +111,10 @@ impl Component for Tree { type Properties = Props; fn create(props: Self::Properties, _link: ComponentLink) -> Self { - Tree { props } + Tree { + props, + previous_expanded_state: Default::default(), + } } fn update(&mut self, _msg: Self::Message) -> ShouldRender { @@ -136,7 +141,9 @@ impl Component for Tree { html! {
- {nodes} +
    + {nodes} +
} } @@ -145,42 +152,28 @@ impl Component for Tree { impl Tree { fn render_children(&self, node_id: &NodeId, depth: u32) -> yew::virtual_dom::VNode { let tree = self.props.tree.borrow(); + let node = tree.get(node_id).unwrap(); + let children = node.children(); - let nodes = tree - .children_ids(node_id) - .unwrap() + let nodes = children + .iter() .map(|node_id| { + let key = { + let mut hasher = DefaultHasher::new(); + node_id.hash(&mut hasher); + hasher.finish() + }; let node = tree.get(node_id).unwrap(); let data = node.data(); - let on_collapse = { - let node_id = node_id.clone(); - self.props.on_collapse.clone().map(move |x| { - x.reform(move |event: MouseEvent| { - event.stop_propagation(); - (node_id.clone(), event) - }) - }) + let previous_is_expanded = self + .previous_expanded_state + .borrow_mut() + .insert(key, data.is_expanded); + let inner_nodes = if !data.is_expanded && !previous_is_expanded.unwrap_or(true) { + Default::default() + } else { + self.render_children(node_id, depth + 1) }; - let on_expand = { - let node_id = node_id.clone(); - self.props.on_expand.clone().map(move |x| { - x.reform(move |event: MouseEvent| { - event.stop_propagation(); - (node_id.clone(), event) - }) - }) - }; - let onclick = { - let node_id = node_id.clone(); - self.props - .onclick - .clone() - .map(move |x| x.reform(move |event| (node_id.clone(), event))) - }; - let inner_nodes = self.render_children(node_id, depth + 1); - let mut hasher = DefaultHasher::new(); - node_id.hash(&mut hasher); - let key = hasher.finish(); html! { Tree { is_selected=data.is_selected label=data.label.clone() secondary_label=data.secondary_label.clone() - on_collapse=on_collapse - on_expand=on_expand - onclick=onclick + on_collapse=self.props.on_collapse.clone() + on_expand=self.props.on_expand.clone() + onclick=self.props.onclick.clone() depth=depth + node_id=node_id.clone() key=key > {inner_nodes} @@ -205,20 +199,19 @@ impl Tree { }) .collect::(); - html! { -
    - {nodes} -
- } + return nodes; } } struct TreeNode { props: TreeNodeProps, + handler_caret_click: Callback, + handler_click: Callback, } -#[derive(Clone, PartialEq, Properties)] +#[derive(Clone, Properties)] struct TreeNodeProps { + node_id: NodeId, disabled: bool, has_caret: bool, icon: Option, @@ -228,27 +221,85 @@ struct TreeNodeProps { is_selected: bool, label: yew::virtual_dom::VNode, secondary_label: Option, - on_collapse: Option>, - on_expand: Option>, - onclick: Option>, + on_collapse: Option>, + on_expand: Option>, + onclick: Option>, children: html::Children, depth: u32, } +impl PartialEq for TreeNodeProps { + fn eq(&self, other: &Self) -> bool { + self.node_id == other.node_id + && self.disabled == other.disabled + && self.has_caret == other.has_caret + && self.icon == other.icon + && self.icon_color == other.icon_color + && self.icon_intent == other.icon_intent + && self.is_expanded == other.is_expanded + && self.is_selected == other.is_selected + && self.label == other.label + && self.secondary_label == other.secondary_label + && self.on_collapse == other.on_collapse + && self.on_expand == other.on_expand + && self.onclick == other.onclick + && self.depth == other.depth + && (!other.is_expanded || self.children == other.children) + } +} + +enum TreeNodeMessage { + CaretClick(MouseEvent), + Click(MouseEvent), +} + impl Component for TreeNode { - type Message = (); + type Message = TreeNodeMessage; type Properties = TreeNodeProps; - fn create(props: Self::Properties, _link: ComponentLink) -> Self { - TreeNode { props } + fn create(props: Self::Properties, link: ComponentLink) -> Self { + TreeNode { + handler_caret_click: link.callback(TreeNodeMessage::CaretClick), + handler_click: link.callback(TreeNodeMessage::Click), + props, + } } - fn update(&mut self, _msg: Self::Message) -> ShouldRender { - true + fn update(&mut self, msg: Self::Message) -> ShouldRender { + if self.props.disabled { + return false; + } + + match msg { + TreeNodeMessage::CaretClick(event) => { + if self.props.is_expanded { + if let Some(on_collapse) = self.props.on_collapse.as_ref() { + event.stop_propagation(); + on_collapse.emit((self.props.node_id.clone(), event)); + } + } else if let Some(on_expand) = self.props.on_expand.as_ref() { + event.stop_propagation(); + on_expand.emit((self.props.node_id.clone(), event)); + } + } + TreeNodeMessage::Click(event) => { + if let Some(onclick) = self.props.onclick.as_ref() { + onclick.emit((self.props.node_id.clone(), event)); + } + } + } + + false } fn change(&mut self, props: Self::Properties) -> ShouldRender { if self.props != props { + // crate::log!( + // "rerender {:?} {} {:?}", + // self.props.node_id, + // self.props.children == props.children, + // self.props.icon, + // ); self.props = props; true } else { @@ -266,7 +317,7 @@ impl Component for TreeNode { html! {
  • -
    +
    { if self.props.has_caret { let mut class = Classes::from("bp3-tree-node-caret"); @@ -280,15 +331,7 @@ impl Component for TreeNode { } } else { @@ -313,7 +356,9 @@ impl Component for TreeNode { }
    - {self.props.children.clone()} +
      + {self.props.children.clone()} +
  • }