Is your feature request related to a problem? Please describe.
In order to create a component that has a bit of smarts in it, a developer needs to store the props, link, and probably some callbacks and state into the component struct.
Describe the solution you'd like
Each component lifecycle method should pass in a reference to the link and props to alleviate the burden on the developer to store those values themselves.
pub struct Context<COMP> {
link: ComponentLink<COMP>,
props: COMP::Properties,
}
pub trait Component: Sized + 'static {
type Message: 'static;
type Properties: Properties;
fn create(ctx: &Context<Self>) -> Self;
fn mounted(&mut self, ctx: &Context<Self>) -> ShouldRender;
fn update(&mut self, ctx: &Context<Self>, msg: Self::Message) -> ShouldRender;
fn change(&mut self, ctx: &Context<Self>, props: &Self::Properties) -> ShouldRender;
fn view(&self, ctx: &Context<Self>) -> Html;
fn destroy(&mut self, ctx: &Context<Self>) -> ();
}
I believe this would also remove the need to have props implement cloneable
Wouldn't the signature for change be the following instead to remove the now redundant props, given that the ctx associated with the component could be updated behind the scenes:
fn change(&mut self, ctx: &Context<Self>) -> ShouldRender;
EDIT: I think managing
changemight be difficult, because it might be difficult to keep around an old props to compare against new props (which is what I assume the proposed signature attempts to support).
Also, some methods on ComponentLink require a mutable reference, eg:
pub fn send_message(&mut self, msg: COMP::Message)
Its conceivable that you could just clone the borrowed reference to link to gain ownership if you wanted to call that method, but that method also superficially requires &mut self and could be just changed to require &self if wanted.
I'm initially not a huge fan of complicating the function signatures, but notably, only the update method would have to gain an argument and create would lose one, and in exchange Yew would stop cloning data all over the place. Additionally it sort of settles what needs how to properly handle where to put props - in that it is now decided for you by the framework - unless you need to clone a specific field for some reason.
When specialization rolls around, create could have a default implementation using Default::default, leaving just update and view to be implemented by hand, given that change's default of returning true should be adequate 90%+ of the time. In the case of pure components, just view would need to be implemented.
Overall - a huge win for removing boilerplate.
I assume that Context<Self> will be owned by one of those xyzState objects under the hood and passed around borrowed references. if one would want to mutate something inside of props, how would that be possible? Or would this now mean that we'll have to keep Context inside of the component struct?
I assume that you would clone prop references to acquire ownership of their values and store them in the Model if you wanted to mutate them later.
@hgzimmerman any chance you could share a pseudo-code example of how you image that would be? Just want to wrap my head around what this change might eventually look like.
I assume that
Context<Self>will be owned by one of thosexyzStateobjects under the hood and passed around borrowed references. if one would want to mutate something inside of props, how would that be possible? Or would this now mean that we'll have to keepContextinside of the component struct?
Part of the idea is that props would be immutable. Component state would be separate and would be mutable.
@jstarry wouldn't that mean such design would be contradictory to how html5 works? How would one go about changing say <input value="John">. By its design nature it is suppose to be mutable. I am applying same logic to custom components.
From reddit (https://www.reddit.com/r/rust/comments/ektzvd/announcing_the_v011_release_of_yew/fddpiqx/)
druid is using something similar https://docs.rs/druid/0.4.0/druid/widget/trait.Widget.html on their equivalent functions. It even goes so far to have distinct context versions for each trait function like UpdateContext or MountContext etc. Its quite more explicit that way without hiding it behind "magic" macros.
Another thing to consider here is making it easier to hold onto agent bridges and service tasks. Right now, devs need to fill their Component with boxed Tasks and Bridges.
if one would want to mutate something inside of props, how would that be possible? Or would this now mean that we'll have to keep
Contextinside of the component struct?
@trivigy I'm doing this in the application I'm currently working on, modifying a property inside Props in the create() method (and applying this modification again in the change() method). I'm going to assume it should be possible to work around this by cloning this property into the model like @hgzimmerman suggests and making changes to it there.
Most helpful comment
Wouldn't the signature for
changebe the following instead to remove the now redundantprops, given that thectxassociated with the component could be updated behind the scenes:Also, some methods on
ComponentLinkrequire a mutable reference, eg:Its conceivable that you could just clone the borrowed reference to link to gain ownership if you wanted to call that method, but that method also superficially requires
&mut selfand could be just changed to require&selfif wanted.I'm initially not a huge fan of complicating the function signatures, but notably, only the
updatemethod would have to gain an argument andcreatewould lose one, and in exchange Yew would stop cloning data all over the place. Additionally it sort of settles what needs how to properly handle where to put props - in that it is now decided for you by the framework - unless you need to clone a specific field for some reason.When specialization rolls around,
createcould have a default implementation usingDefault::default, leaving justupdateandviewto be implemented by hand, given thatchange's default of returning true should be adequate 90%+ of the time. In the case of pure components, justviewwould need to be implemented.Overall - a huge win for removing boilerplate.