yewstack / yew

Rust / Wasm framework for creating reliable and efficient web applications
https://yew.rs
Apache License 2.0
30.44k stars 1.42k forks source link

Sub-component property value not updating on component render #610

Closed jdgwf closed 5 years ago

jdgwf commented 5 years ago

Description

I'm submitting a bug report

I have a subcomponent which isn't updating the .value property I've created when an event is fired... I have source code and running example here: http://rust-yew-child-component-test.jdgwf.com/

Expected Results

On component render I'd expect that sub-component properties would be updated along with the labels within the re-rendered component.

For example, on the"working" example when I press "Reset Text" "Reset!" should appear both in the label below and in the text field called in the custom sub-component.

Full "working" source code here: https://github.com/jdgwf/rust-yew-child-component-test

Actual Results

The text label changes, but not the sub-component's value.

Context (Environment)

hgzimmerman commented 5 years ago

Based on a quick skim of your project I found that you don't set your LabelInput's field's to those of the props when change() is called. When a parent updates its state and re-renders components present in its view, it re-passes props to those components, but instead of creating new instances of those components, it merely updates them using change(), which is more efficient.

Try changing LabelInput's change() method to the following:

    fn change(&mut self, props: Self::Properties) -> ShouldRender {
        self.label_text = props.label_text;
        self.state_value = props.state_value;
        self.onsignal = props.onsignal;
        true
    }

This can be seen as annoying, boilerplate-y, and doesn't meaningfully improve performance because you re-render always. A pattern I have seen is to have your Component have a props: Props field instead of manually copying every member of your props to your component. If your props have PartialEq implemented for it, then updating could be as simple as:

    fn change(&mut self, props: Self::Properties) -> ShouldRender {
        let changed = self.props != props;
        if changed {
            self.props = props;
        }
        changed
    }
jdgwf commented 5 years ago

That got it with a few modifications to the local variable names!

    fn change(&mut self, props: Self::Properties) -> ShouldRender {
        self.label_text = props.label;
        self.state_value = props.value;
        self.onsignal = props.onsignal;
        true
    }

Now I need to figure out why it works like that and burn it to my process.

Thank you! (github and live site updated)

jstarry commented 5 years ago

Sweet, nice eye @hgzimmerman