Skip to content

VTag reuse does not reset ancestors NodeRef #2029

@mc1098

Description

@mc1098

Problem
When a VTag is reused the ancestor's NodeRef doesn't get reset and therefore will continue pointing to the Element in the reused VTag. This is often not a problem, however, when conditionally rendering and using NodeRef on both this can lead to editing a Element reference with a NodeRef that was set to the ancestor VTag. (see the example below)

Steps To Reproduce

use yew::prelude::*;

pub struct Comp {
    show_a: bool,
    node_ref_a: NodeRef,
    node_ref_b: NodeRef,
}

impl Component for Comp {
    type Message = ();
    type Properties = ();

    fn create(_: &Context<Self>) -> Self {
        Self {
            show_a: true,
            node_ref_a: Default::default(),
            node_ref_b: Default::default(),
        }
    }

    fn update(&mut self, _: &Context<Self>, _: Self::Message) -> bool {
        self.show_a = !self.show_a;
        true
    }

    fn view(&self, ctx: &Context<Self>) -> Html {
        let (t, node) = if self.show_a {
            ("a", html! { <div id="a" ref={self.node_ref_a.clone()} /> })
        } else {
            ("b", html! { <div id="b" ref={self.node_ref_b.clone()} /> })
        };

        let onclick = ctx.link().callback(|_| ());

        html! {
            <>
                {t}
                {node}
                <button {onclick}>{ "Click me" }</button>
            </>
        }
    }

    fn rendered(&mut self, _: &Context<Self>, first_render: bool) {
        if first_render {
            return;
        }

        if !self.show_a {
            // This should never be Some!
            if let Some(node) = self.node_ref_a.get() {
                // node_ref_a here is actually referencing node b!
                node.set_text_content(Some("hello b!"));
            }
        }
    }
}

First render is fine, the second after pressing the button will render the "b" div and then rendered will run and even though it's using the node_ref_a it will return a Some with the reference to the "b" div and the content will be updated to display "hello b!".

Expected behavior
That the ancestors NodeRef is reset when the Element is being reused, this simply means updating the ancestors NodeRef and setting the inner Option<Node> to None.

When working correctly the example will never set the text content for either div "a" or "b" to "hello b!".

Environment:

  • Yew version: master

Questionnaire

  • I'm interested in fixing this myself but don't know where to start
  • I would like to fix and I have a solution
  • I don't have time to fix this right now, but maybe later

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions