Skip to content
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

Component v2 #1646

Closed
wants to merge 8 commits into from
Closed

Component v2 #1646

wants to merge 8 commits into from

Conversation

jstarry
Copy link
Member

@jstarry jstarry commented Nov 15, 2020

Description

This PR aims to address two common issues with Components:

  1. Too much boilerplate when implementing components
  2. Easy to forget to update props inside the change lifecycle method

This PR addresses the two above issues with breaking changes and renames and deprecates the existing Component trait (now named LegacyComponent) to encourage devs to switch to the new trait.

Fixes #830

Changes

  • New Component trait!
  • Component change method renamed to changed (and is only called when props != new props)
  • Component methods are now passed a Context which right now is just an alias to Scope
  • Renamed yew::html::Component to LegacyComponent
  • Updated html! to only handle new components so legacy components must be wrapped in Legacy<..>
  • Properties must now implement PartialEq
  • Removed a bunch of stdweb examples because who has the time..

Checklist

  • Write new docs
  • I have run cargo make pr-flow
  • I have reviewed my own code

@jstarry
Copy link
Member Author

jstarry commented Nov 15, 2020

@siku2 any thoughts?

@siku2
Copy link
Member

siku2 commented Nov 17, 2020

Hmm... The whole wrap! macro doesn't seem very ergonomic to me. Is it possible to implement Component on the state directly (like we have it now) but instead of accepting a receiver (self) the trait methods accept a Context which is basically Wrapper but more transparent?

I'm imagining something like this:

// Context contains props, state, and the component link

struct Props {
    init_text: String
}

struct Foo {
    counter: usize
}
impl Component for Foo {
    type Properties = Props;
  
    fn update(ctx: &mut Context, msg: Self::Message) -> bool {
        match msg {
             Msg::Increment => {
                ctx.state.counter += 1;
                true
             }
        } 
    } 

    fn view(ctx: &Context) -> Html {
        html! { <span>{ &ctx.props.text }</span> }
    }
}

@jstarry
Copy link
Member Author

jstarry commented Nov 17, 2020

@siku2 yeah thanks for talking some sense into me. That's a better solution than using more magic macros, I'll switch to that 👍

@jstarry jstarry changed the title Introduce ergonomic wrapped components Component v2 Nov 22, 2020
use yew::component::{Component, Context};
use yew::{html, Html, Legacy, ShouldRender};

type Slider = Legacy<LegacySlider>;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't migrate this Slider intentionally to show how a project could slowly migrate away from the legacy component trait

@github-actions
Copy link

github-actions bot commented Nov 22, 2020

Visit the preview URL for this PR (updated for commit 53fe13d):

https://yew-rs--pr1646-yew-comp-3eimqgb8.web.app

(expires Tue, 01 Dec 2020 14:39:33 GMT)

🔥 via Firebase Hosting GitHub Action 🌎

@jstarry jstarry marked this pull request as ready for review November 22, 2020 15:23
@jstarry jstarry requested a review from siku2 November 22, 2020 15:36
Copy link
Member

@siku2 siku2 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I love it!

use crate::html::{Html, Properties, Scope, ShouldRender};

/// Context
pub type Context<T> = Scope<T>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think all these type aliases only add confusion... Is there a good reason why we don't use the same name everywhere?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm good point. I will just move yew::html::Scope to yew::component::Context.

@@ -1,4 +1,4 @@
#![allow(clippy::needless_doctest_main)]
#![allow(clippy::needless_doctest_main, deprecated)]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't #[allow(deprecated)] be applied locally so we don't accidentally end up using deprecated API of another crate in the future?

yew/src/app.rs Show resolved Hide resolved
since = "0.18.0",
note = "Please switch to the yew::component::Component trait"
)]
pub trait LegacyComponent: Sized + 'static {
Copy link
Member

@siku2 siku2 Nov 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Switching to LegacyComponent seems like more of a pain than just upgrading to the new Component trait. Is there anything I'm missing that would make the transition impossible?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was the big dilemma I had when making these changes. Ideal situation would be:

  1. Create new Component interface which works well with yew-macro
  2. Deprecate existing Component interface but have it still be compatible with yew-macro

But these two are conflicting at the macro level. I decided to prioritize the new interface experience over having a nice deprecated experience but try my best to minimize changes needed to switch to the legacy component interface.

I opted to rename to LegacyComponent to make it obvious to devs which components have been upgraded but I don't feel too strongly about that. The main annoying thing is the macro changes to wrap all the html! components with Legacy<..>. Perhaps it is too annoying. I really care about this upgrade being smooth because upgrading to the new Component is tedious.

Call me crazy, but @siku2 what do you think about adding a new macro? It would act the same as html! but would handle the new Component interface. I'm thinking it could be called vdom!

Copy link
Member

@siku2 siku2 Nov 28, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel very conflicted about this. I very much understand the need to make the transition as seamless as possible but we might be worrying a bit too much about this given that Yew is officially still at major version zero (i.e. initial development).

To quote semver.org:

If you’re worrying a lot about backwards compatibility, you should probably already be 1.0.0.

Adding a new macro is a step too far in my opinion. If you think the migration to the new component trait is too tedious, I'm fine with keeping LegacyComponent around until the following release. We can deal with the Legacy<_> wrapper by suggesting the use of a type alias type MyActualComponent = Legacy<MyComponentImplementation>.

The migration guide could look something like this:

  1. Replace impl Component with impl LegacyComponent
  2. Rename your component Foo to something like FooComp locally
  3. Create a type alias pub type Foo = Legacy<FooComp>;
  4. Use the Foo type instead of the FooComp struct

Personally though, I wouldn't even consider switching to LegacyComponent, if I have to do some modifications in each file anyway, I might as well upgrade to the new Component trait.

route::Route,
switch::{Routable, Switch},
};
pub use crate::switch::Switch as Routable;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we actually rename Switch -> Routable and then create a deprecated type alias for Switch.

#[deprecated(note="use `Routable` instead")]
pub type Switch = Routable;

yew-functional/src/use_context_hook.rs Outdated Show resolved Hide resolved
yew-functional/src/use_context_hook.rs Outdated Show resolved Hide resolved
jstarry and others added 2 commits November 24, 2020 22:36
Co-authored-by: Simon <simon@siku2.io>
Co-authored-by: Simon <simon@siku2.io>
Co-authored-by: Teymour Aldridge <42674621+teymour-aldridge@users.noreply.github.com>
@frostu8
Copy link

frostu8 commented Apr 7, 2021

I really do love this. I feel like the clunkiest part of writing components was the ComponentLink and Properties. Now that the component is directly managed by its own concrete type, that means less boilerplate, more fun, and also enables some other things that could be implemented in the future.

@hamza1311
Copy link
Member

I think we should just remove LegacyComponent. There has already been a good amount of breaking changes so what's one more?

Also, what's left to be done in this PR? Is there any way I could help out?

@siku2
Copy link
Member

siku2 commented Jul 19, 2021

I think we should just remove LegacyComponent. There has already been a good amount of breaking changes so what's one more?

Agreed!

Also, what's left to be done in this PR? Is there any way I could help out?

Feel free to take over this PR

@hamza1311 hamza1311 mentioned this pull request Jul 19, 2021
3 tasks
@mc1098
Copy link
Contributor

mc1098 commented Aug 5, 2021

Should this now be closed in favour of #1961?

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

Successfully merging this pull request may close these issues.

Too much boilerplate for components
6 participants