-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Blazor API Review: Components Programming Model #11610
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
Comments
@SteveSandersonMS @javiercn @pranavkm @mkArtakMSFT @danroth27 @ajaybhargavb @NTaylorMullen - please have a look and give your feedback 👍 |
+1 For
Seems fairly convenient to have the sync overloads. Are there particularly good reasons not to have these? We should have the async ones call the sync one and be done.
The doc comment says it's initializing the component. I'd think it's generally ucommon for users to be interacting with with
|
ComponentBase
Ya if we just had two
Sounds reasonable to me; however, it's also not critical because that's something we can always add in the future.
It's convenient but I'm less convinced it's needed right now. In TagHelper land we did this and people loved having the synchronous overload. However, people also got massively confused at the fact that TagHelpers were async even though all of their code was written in a synchronous method. I'm team, don't have both because of my scars from TagHelper times 😆
+1 for doing it in the future. We don't want to overcommit here because things can get sloppy real quick if we expose too many hooks too early. Events and EventCallback
Ehh, less is more imo. If we get feedback we can always add that.
Wont we have a Web specific assembly? Any reason the extension methods can't exist there? General feedback on Events and EventCallbackDo we expect people to be directly interacting with any of the EventCallback types? I imagine they wouldn't. If that's the case we should consider sprinkling additional EditorBrowsableState.Nevers on more of the API surface. Parameters
So I don't have a lot of context in this stack but based off of the API solely it looks more like a
I'd just do
Definitely. I think in general the API should be slimmed down to conform to
Ya i'd imagine this would just be part of Attributes
Thinking about this more I feel like this should be represented as its own own return type that has helper methods to extract attribute names, attribute kvp's attribute values etc. Anyhow, that's a bigger ask than an API review, I think I just have beef having Fundamental building blocks
If this is used with the
Why does this have to be public? Is it not a runtime concern where we're ok with it not existing in the ref assembly? |
Agreed - doesn't seem to serve any purpose.
Could be added in the future; don't need to now. In the short term people who need it can set things on captured variables to achieve the same affect (albeit with some perf cost).
I'd be fine with moving to
It seems like the best option still. C# isn't good at dealing with this "multiple possible return types" scenario (unlike, say, TypeScript), so the sync variants are needed if we want to avoid people having to put
I've overridden this as a way to enforce rules like "parameter X is required" because it runs before
This would need more design, as it's subtle. Calling it If the only common use case for this is on
Feels like that's a matter of terminology. If we change the name of
I think there was just no reason not to, and it saves one allocation per component instance. Any cause for concern?
I thought the name We could call it
+1
I would have expected
That's not how I was thinking of it. I thought we were describing the notion of UI events in a more abstract way, and they would still apply to other rendering technologies (such as how I gave meanings to them in the Flutter prototype). I know it just so happens that they precisely match HTML/DOM so it's not really honest to say it's more abstract. Is it practical for us to have only
As suggested above, maybe we embrace that fact that there's only one implementation, call it
Don't mind
It can't implement
Sure, should be a regular method.
Any reason it should do? If people consume it through
+1
Agreed. The
Sure
Before now, I would have agreed with Taylor's point that it's good to use the |
It's a limitation in System.Text.Json - we have to be able to deserialize incoming JSON representations of these references. If they can now deserialize |
It won't deserialize private things, but we can now write a custom converter, so we should do that. |
Wait it really can't deserialize private classes? |
It can deserialize private classes, they don't look at private properties. |
Ah ok |
There's a little subtle wierdness here. Usually if we provide sync and async methods the async one calls the sync one. However, we call both, so if you override However, this is also necessary because we expect inheritance to work with components. If you override As another strange consequence, if you override I think this area is worth more thought. |
Part of: #11610 This change brings forward the Dispatcher as a more primary and more expandable concept. - Dispatcher shows up in more places - Dispatcher is an abstract class for horizontal scalability - Dispatcher has parallels with S.Windows.Threading.Dispatcher where possible Looking for feedback on this approach. I feel pretty strongly that making this an abstract class is the right choice, I want to see opinions on how much to push it into people's faces.
Do you mean that they appear on the renderer and on IDispatcher? Could we make anything the user doesn't interact with internal/protected/private? AFAIK, the only thing that calls invoke is the RemoteRenderer could we make Invoke and InvokeAsync protected on the renderer and have the render handle expose it to components?
I personally prefer OnInit as its more inline with what other frameworks have, but I don't feel strongly about it.
Is it useful to override it, do something before and then call base to get the default behavior?
We discussed this in the past in depth and agreed that this was not necessary and that we can add it later if we have to. JS interop is rare except in the case you are building a component framework and at that point you can simply extend ComponentBase, add this yourself and then use this as the base class for your components.
Id rather hide the dispatcher from the user as much as possible. We should look into what the requirement is for the dispatcher to be public and see if we can take care of that.
I'm fine with configure, I feel that if we decide it to call it ThisSuperSpecificThing and we find we need to use it for something else in the future then we are "locked". I would suggest Attach as an alternative, as in this attaches the component to the render, but I feel Configure is fine.
I would be happier if we could completely hide IDispatcher from the user, not sure if its possible. The main case for IDispatcher is to do Invoke(()= > StateHasChanged()) to trigger a new render, if we can avoid having the concept that's better.
Could this be made to use the struct enumerator optimization? Is that something we need to add now?
I'm fine with this. Should we go into more detail on other APIs? RenderTree, RenderTreeBuilder, etc? |
There are good reasons why we do this, base on the experience we want to achieve in multiple scenarios. The general principle is that we want to trigger a render before any async work happens and then we want to trigger an additional render after the async work completes. That is for example to allow scenarios where a developer is populating a grid, to produce an initial render with the head of the grid and no elements and to produce an additional render with the rows of the grid. There is a small weirdness as you pointed out, which needs to be documented, but I believe this is a corner case, a base class can always override both methods and seal one of them so it can't be overriden by derived classes. For example a base class can override both, oninit and oninitasync and seal oninit. I am with Steve in that we don't want people to have to around filling their apps with Task.CompletedTask or disabling warnings for async methods not awaiting anything. So I'm happy with the trade-off we took here. |
Part of: #11610 This change brings forward the Dispatcher as a more primary and more expandable concept. - Dispatcher shows up in more places - Dispatcher is an abstract class for horizontal scalability - Dispatcher has parallels with S.Windows.Threading.Dispatcher where possible Looking for feedback on this approach. I feel pretty strongly that making this an abstract class is the right choice, I want to see opinions on how much to push it into people's faces.
Part of: #11610 This change brings forward the Dispatcher as a more primary and more expandable concept. - Dispatcher shows up in more places - Dispatcher is an abstract class for horizontal scalability - Dispatcher has parallels with S.Windows.Threading.Dispatcher where possible Looking for feedback on this approach. I feel pretty strongly that making this an abstract class is the right choice, I want to see opinions on how much to push it into people's faces.
Part of: #11610 This change brings forward the Dispatcher as a more primary and more expandable concept. - Dispatcher shows up in more places - Dispatcher is an abstract class for horizontal scalability - Dispatcher has parallels with S.Windows.Threading.Dispatcher where possible Looking for feedback on this approach. I feel pretty strongly that making this an abstract class is the right choice, I want to see opinions on how much to push it into people's faces.
* Design concept for Dispatcher Part of: #11610 This change brings forward the Dispatcher as a more primary and more expandable concept. - Dispatcher shows up in more places - Dispatcher is an abstract class for horizontal scalability - Dispatcher has parallels with S.Windows.Threading.Dispatcher where possible Looking for feedback on this approach. I feel pretty strongly that making this an abstract class is the right choice, I want to see opinions on how much to push it into people's faces. * WIP * PR feedback
Part of: #11610 This change just renames the type, because it's used in the compiler. We will need to react in the compiler, and then update all of the usage here.
Part of: #11610 This change just renames the type, because it's used in the compiler. We will need to react in the compiler, and then update all of the usage here.
* Blazor API Review: Parameters Part of #11610
Fixes: #11610 I took the approach here of building this into `ComponentBase` instead of `IHandleAfterRender` - *because* my reasoning is that `firstTime` is an opinionated construct. There's nothing fundamental about `firstTime` that requires tracking by the rendering, it's simply an opinion that it's going to be useful for component authors, and reinforces a common technique. Feedback on this is welcome.
Fixes: #11610 I took the approach here of building this into `ComponentBase` instead of `IHandleAfterRender` - *because* my reasoning is that `firstTime` is an opinionated construct. There's nothing fundamental about `firstTime` that requires tracking by the rendering, it's simply an opinion that it's going to be useful for component authors, and reinforces a common technique. Feedback on this is welcome.
Fixes: #11610 I took the approach here of building this into `ComponentBase` instead of `IHandleAfterRender` - *because* my reasoning is that `firstTime` is an opinionated construct. There's nothing fundamental about `firstTime` that requires tracking by the rendering, it's simply an opinion that it's going to be useful for component authors, and reinforces a common technique. Feedback on this is welcome.
Fixes: #11610 I took the approach here of building this into `ComponentBase` instead of `IHandleAfterRender` - *because* my reasoning is that `firstTime` is an opinionated construct. There's nothing fundamental about `firstTime` that requires tracking by the rendering, it's simply an opinion that it's going to be useful for component authors, and reinforces a common technique. Feedback on this is welcome.
Fixes: #11610 I took the approach here of building this into `ComponentBase` instead of `IHandleAfterRender` - *because* my reasoning is that `firstTime` is an opinionated construct. There's nothing fundamental about `firstTime` that requires tracking by the rendering, it's simply an opinion that it's going to be useful for component authors, and reinforces a common technique. Feedback on this is welcome.
Blazor API Review: Components programming model
This is the API for the primary programming surface for authoring components. This includes types and attributes that make up
ComponentBase
as well as types that you will use likeMarkupString
andElementRef
. This excludes feature areas likeIUriHelper
,RenderTreeBuilder
, or auth which get their own API review.ComponentBase
Some topics to discuss here:
Invoke
andInvokeAsync
are different method instead of overloads (here, onIDispatcher
andRenderHandle
).Should we have overloads ofInvoke
andInvokeAsync
that return something here as well?OnInit
vsOnInitialized
? Abbreviations in APIs always feel bad to me, even if they are known things likeOnInit
.Are we sure that it's worth providing sync and async versions of all of our lifecycle methods?SetParametersAsync
should probably be an explicit implementation, the user cannot duplicate the behaviour of theComponentBase
implementation, so it's not useful to override.We don't expose any lifecyle methods related toIHandleEvents
(other thanShouldRender
). That's probably ok? it could be done in the future.HasRendered
- basically provide some API for the common use case of initializing JS interop?RenderHandle
seems to wear two hats. Should we pass the dispatcher into the component instead?RenderHandle
to be a struct?Configure
as a name. Other ideas?Events and EventCallback
Some topics to discuss here:
EventCallbackWorkItem
should be readonly.EventCallbackWorkItem
is needed to keep the API from being insane due to generics, but I feel like it doesn't provide any useful information.ShouldEventCallback
andEventCallback<>
have anInvokeAsync()
method? It feels bad to pass null to the existing method.EventCallback<>
should probably have anEmpty
member for consistency.public string Create<T>(object receiver, string callback) { throw null; }
should be removed.We decided to leaveEventCallbackFactoryBinderExtensions
- we have a problem here. All of our existingbind
functionality for elements is tied toUIChangeEventArgs
which is an HTML/DOM concept. I have no brilliant ideas what to do about this.UIChangeEventArgs
where it is for now.Dispatcher
Some topics to discuss here:
ComponentBase
orRenderHandle
(or both).Func<TResult>
andFunc<Task>
.Parameters
Some topics to discuss here:
Parameter
is reallyParameterState
orParameterValue
.Low value, not doing.Cascading
->IsCascadingValue
?ParameterCollection
doesn't implement any interfaces, this could easily implementIReadOnlyDictionary<>
and then we don't needToDictionary()
. edit we're renaming this instead.SetParameterProperties
is public, and extension method.This is OK, there is predendent for this in the BCLParameterEnumerator
doesn't implementIEnumerator
.Attributes
Some topics to discuss here:
LayoutAttribute
should probably be in.Components
Inherited = true
for all byRouteAttribute
(which has a good reason to prevent inheritance)Fundamental building blocks
Some topics to discuss here:
ElementRef
beElementReference
?The text was updated successfully, but these errors were encountered: