Skip to content

feat(core): memoize components by value of Element props - #5756

Open
rexlunae wants to merge 1 commit into
DioxusLabs:mainfrom
rexlunae:feat/memoize-element-props
Open

feat(core): memoize components by value of Element props#5756
rexlunae wants to merge 1 commit into
DioxusLabs:mainfrom
rexlunae:feat/memoize-element-props

Conversation

@rexlunae

Copy link
Copy Markdown

Closes #1929

Problem

When a component re-renders and passes an Element (e.g. children) to a child component, the Element is rebuilt from scratch on every render. VNode's PartialEq was pointer-based (Rc::ptr_eq), so the new allocation never compared equal to the old one — the child component re-rendered even when its props were completely unchanged.

fn app() -> Element {
    let mut show = use_signal(|| false);
    rsx!(
        div {
            p { "{show}" }
            Whatever { div { } } // rebuilt each render, new pointer
        }
    )
}

#[component]
fn Whatever(children: Element) -> Element {
    // re-ran on every app re-render, even though children never changed
    rsx!(div { {children} })
}

Change

  • VNode's PartialEq now compares by value:
    • template compared by its content hash,
    • dynamic attributes compared by value,
    • dynamic nodes compared structurally (text, placeholders, fragments, and components — component props are compared with the same type-erased equality the renderer already uses for component memoization).
  • The diff fast path in diff/node.rs keeps pointer equality (VNode::ptr_eq), since the diff algorithm walks the tree structurally and a value comparison there would be wasted work.
  • The FragmentProps doc comment is updated: children are now memoized by value, so borrowing nodes from a parent no longer defeats memoization.

Tests

  • tests/memoize_element_props.rs: a child component with unchanged children does not re-render across parent re-renders; it does re-render when the children's content changes.
  • tests/diff_dynamic_node.rs (toggle_template, regression for bug: Panic on diff/node.rs #2815): updated — the first phase (parent re-render) now correctly produces no edits because the child is memoized; the placeholder/text toggling is still covered by driving the child scope directly.

All dioxus-core tests pass (including the fuzzing and kitchen-sink suites); clippy and fmt clean.

When a component re-renders and passes an Element to a child component, the
Element is rebuilt from scratch, so pointer equality always fails and the
child re-renders even when its props are unchanged.

Make VNode's PartialEq compare by value instead of by allocation identity:
the template is compared by its content hash, dynamic attributes by value,
and dynamic nodes structurally (text, placeholders, fragments, and
components — component props are compared with the same type-erased
equality used for component memoization).

The diff fast path intentionally keeps pointer equality (Rc::ptr_eq), since
the diff algorithm walks the tree structurally and a value comparison there
would be wasted work.

Closes DioxusLabs#1929
@rexlunae

Copy link
Copy Markdown
Author

cc @ealmloff @jkelleyrtp — this makes Element props memoizable by value (issue #1929). I kept the diff fast path on pointer equality so the renderer's hot path is untouched.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

enhancement: Implement memoization by value of Elements passed as props

1 participant