-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Replace mounted with rendered lifecycle method #1072
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
I didn't add tests 😱 but @ZainlessBrombie you can start using this branch to make progress on |
Excellent! :) |
Uuh one of my old tests just started failing after merge D: |
So I think the following test should not fail, but it does: #[wasm_bindgen_test]
fn message_sent_if_component_destroyed() {
#[derive(Properties, Clone)]
struct UpdateCalledProps {
update_called: Rc<dyn Fn()>,
}
struct MessageShouldArrive {
link: ComponentLink<Self>,
props: UpdateCalledProps,
}
impl Component for MessageShouldArrive {
type Message = ();
type Properties = UpdateCalledProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
MessageShouldArrive { link, props }
}
fn update(&mut self, msg: Self::Message) -> bool {
(self.props.update_called)();
true
}
fn change(&mut self, _props: Self::Properties) -> bool {
true
}
fn view(&self) -> Html {
self.link.send_message(());
return html! {
<div>{":)"}</div>
};
}
}
use std::cell::RefCell;
struct MessageComponentWrapper {
once: RefCell<bool>,
link: ComponentLink<Self>,
props: UpdateCalledProps,
}
impl Component for MessageComponentWrapper {
type Message = ();
type Properties = UpdateCalledProps;
fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
MessageComponentWrapper {
once: RefCell::new(true),
link,
props,
}
}
fn update(&mut self, msg: Self::Message) -> bool {
true
}
fn change(&mut self, _props: Self::Properties) -> bool {
true
}
fn view(&self) -> Html {
if *self.once.borrow_mut().deref() {
*self.once.borrow_mut().deref_mut() = false;
self.link.send_message(());
return html! {
<MessageShouldArrive update_called=self.props.update_called.clone() />
};
}
return html! {
<div>{"done"}</div>
};
}
}
let app: App<MessageComponentWrapper> = yew::App::new();
let destroy_counter = Rc::new(std::cell::RefCell::new(0));
let destroy_country_c = destroy_counter.clone();
app.mount_with_props(
yew::utils::document().get_element_by_id("output").unwrap(),
UpdateCalledProps {
update_called: Rc::new(move || *destroy_country_c.borrow_mut().deref_mut() += 1),
},
);
assert_eq!(1, *destroy_counter.borrow().deref());
} |
It only fails on this new branch |
@ZainlessBrombie sounds like that is due to:
This change will ignore update messages if the component has been destroyed |
I see, makes sense. I was able to work around that, too. <div>
<ExampleContextProvider context=ExampleContext("wrong1".into())>
<div>{"ignored"}</div>
</ExampleContextProvider>
<ExampleContextProvider context=ExampleContext("wrong2".into())>
<ExampleContextProvider context=ExampleContext("correct".into())>
<UseContextComponentInner />
</ExampleContextProvider>
</ExampleContextProvider>
<ExampleContextProvider context=ExampleContext("wrong3".into())>
<div>{"ignored"}</div>
</ExampleContextProvider>
<ExpectNoContextComponent />
</div> ExpectNoContextComponent warns in the console if there is a context present. The context is removed in rendered() The console contains the following log data: |
@ZainlessBrombie I see, I think we need each component to have its own scheduler. That's going to be a decent chunk of work but sounds fun :) The problem is that we don't want to call rendered for a component until all of its updates have completed. We also don't want to update any component that is not a descendant of that component until we call rendered on it. That means we have to interleave updates tasks and rendered tasks and so the global scheduler isn't going to work. I propose that the global scheduler has sub schedulers for each component and manages a tree structure of these sub schedulers to know which order to process them in. Does that sound right? |
Lovely!
Exactly. Do you think managing a tree of components in the scheduler will be a performance concern? |
Hmm, I don't think it will be an issue.
Yes, good point. It is breadth first right now, but should be depth first. That will be solvable with the tree approach as well |
breadth right ^^ |
We have a plan! I won't have time for awhile to work on this though. You're welcome to give it a go if you have time! |
If I find the time I'll try, but I'm currently working weekends because we are working on a project to coordinate delivery services for local stores so that might be some time off, idk yet ^^ |
Mmh I could imagine the scheduler having a "layer" counter and a stack of Schedulers. If the layer counter decreases it pops off the scheduler stack by that amount. And increments on push, of course |
@ZainlessBrombie that sounds more important! We can revisit this later :)
Yeah! A stack seems so natural for this. I think it runs into issues when one branch updates another though |
oh right yeah... |
Like I said 😉 |
@ZainlessBrombie I've thought about this the past few days and I have come to the conclusion that it would be better if Yew remained more flexible in rendering and instead move the complexity into the context provider / subscription implementation. Rationale: Parents don't know when their children re-renderThe context provider won't have the opportunity to shove its context in an accessible place for the children components if it never gets rendered. This means that it's the Strict rendering order will likely hinder future featuresRight now, Yew can be pretty flexible in the areas of the component tree that it updates. This could allow us to do some cool async things, optimize diffing, etc. in the future. |
db02589
to
5e44991
Compare
That is a very good point. I hadn't thought about that. |
Yeah, I was thinking that Yew would provide a way for each component to reference its parent. And each component would have a unique id. I think that's enough for the context implementation. When useContext is called, it would be called with the type so no need for reflection |
Yes that should work for a useContext implementation :) |
Problem
No callback for when a component re-renders
Changes
mounted
rendered(&mut self, first_render: bool)
Fixes: #1067
Unblocks: #1057
tag @ZainlessBrombie