-
Notifications
You must be signed in to change notification settings - Fork 10k
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
[Blazor] Strange update behavior #15175
Comments
@arivoir Thanks for contacting us. We're not able to understand your specific scenario/question without a bit more info. Could you provide a link to a github repo with a minimal repro project that showcases your issue? From what I can tell, seems like your parent component is triggering a render and as a result of that render the property in your checkbox is being set, from what I follow it happens in this line Microsoft.AspNetCore.Components.dll!Microsoft.AspNetCore.Components.ComponentBase.SetParametersAsync(Microsoft.AspNetCore.Components.ParameterView parameters) Unknown So the flow likely is:
|
If the answer helps you, feel free to skip the repro and close the issue. |
@javiercn Thanks for your answer. Unfortunately, it is hard for me to reproduce this in an isolated project. Maybe I have time in a future. You said the order is the following
Thanks for this, is the first hint of documentation I found regarding rendering. I really appreciated. This trigger some more doubts. You said in number 2 "A new render tree is produced". Are you referring to the tree of the specific component whose state changed or the whole tree? Clarifying/Documenting about the "Invalidation" mechanism is appreciated. In number 5 you said the SetParameters of the child is called. If 4 determines there are no changes in the child, does 5 happen? if 5 doesn't happen, is the children BuildRenderTree executed? Can it happen that the BuildRenderTree of a parent is executed, but not the children one? |
Your call stack is around here
The algorithm for determining this is quite conservative, but at most it would results in an additional render. It checks the attributes in the order they are declared and determines they are different in 3 situations. The order part is important, but not significant. It improves the performance of this area a lot, (which is critical) and you won't run into it unless you are doing something very sophisticated that renders the same set of attributes in different order. Finally, a way to debug these types of things is to override SetParametersAsync in your components, call base and use breakpoints to trace the flow of events. |
I am much more clear about the relation between parent and children now. Thanks you for that. I will continue thinking on this and try to make my mind around in different scenarios. One related thing it really concerns to me is the StateHasChanged ends up calling the BuildRenderTree synchronously. this is really problematic because the components could end up making a lot of rendering repeatedly. For example, in a component that display a list of items, if a customer writes a loop adding data items, the component has no control of how many items will be added, and it calls StateHasChanged for each item. if this method triggers the rendering synchronously, it will perform a rendering that will finally be discarded. To resolve this problem, many ui platforms (All Xaml, Android, iOS) implement an "Invalidation" pattern. This pattern consist the StateHasChanged only "marks" a component as dirty and the actual rendering is performed asynchronously. This way, calling the StateHasChanged is really cheap, can be called multiple times, and the rendering is performed only one time. |
I’m sorry if it wasn’t clear. That’s more or less how it works. Components simply request a render, the tenderer then decides when to render them. For example, for event handlers we produce a single render batch for the entire event and normally each component gets rendered at most once (when a component has already a pending render queued it doesn’t queue another one, you can see this in componentbase.cs) We have a pending doc issue to do a write-up for this. |
The point is it shouldn't call the render synchronously ever. In one of the components, I'm seeing the BuildRenderTree is called directly
If I call StateHasChanged multiple times it calls BuildRenderTree multiple times too. Do you think this is normal? |
That is not the case https://github.com/aspnet/AspNetCore/blob/master/src/Components/Components/src/ComponentBase.cs#L102-L119 Adding a call to StateHasChanged simply queues the component to be rendered. The renderer decides when the renders happen. This can be triggered by 4 circumstances:
To be very clear, calling StateHasChanged only queues a Render for the component or "marks it as dirty". It's the renderer the one that decides when and how to produce the renders.
I don't want to turn this issue into a discussion as we plan to provide a doc explaining in detail how it works and I think its better if we postpone further discussion until we have a coherent document where everything is explained properly instead of as a collection of ad-hoc questions/comments/answers. Hope this helps clarify things a bit further. If you want to dig in a bit more, I would recommend to create a simple sample and debug through the framework if you want to better understand how the rendering process works. It should be approachable if you use sourcelink, that's what we internally do. |
@javiercn Thanks for providing this information. I want to make some emphasis in 2 points
IMO, the calls to "BuildRendeTree" should be always asynchronous, and they should be skipped if are repeated. |
@javiercn I just found that there are not only problems in the performance. Let me explain my case a little more. I have a component (data-grid) that has 4 panel of cells, each one is a component which renders a grid of cells. In some cases it is necessary to change the range of displayed cells in all the panels, for example if you change the source of the grid. The problem I'm facing is when I call StateHasChanged in one of the "panels", it ends up calling the "BuildRenderTree" of the sibling panel, whose model haven't been updated yet. This is really bizarre. Can you say why happens this? |
@arivoir Look if it is linked to having a delegate as a parameter. I don't know if it relevant in your situation but I mention it because it can cause side-effects on the rendering which is not so obvious. See #13610 (comment). @javiercn It is easy to think that passing a delegate is just an initialization which will not involve the conditions for rerendering and it is never described. Is this something you are planning to look into? I have asked it a few times before on different tasks but never recieved an answer. |
I have an issue and I' trying to understand how Blazor updates work.
I have a component that has a nested component, a lot of them actually.
At some point I call the StateHasChanged of the parent, and a property of one of the children is set.
What is that supposed to be?
Neither the BuildRenderTree of the parent nor the children were performed yet. Therefore the components weren't passed the new parameters yet.
We really need documentation about the rendering. It is extremely complicated to guess what will happen when calling StateHasChanged.
The text was updated successfully, but these errors were encountered: