You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Is your feature request related to a problem? Please describe.
Related to Blazor 0.7.0 and Blazor Components.
The way rendering of Components in a loop is handled causes me some frustration and maybe it could be improved.
I am sure you have probably thought a lot about how to handle this and maybe I am going about it in the wrong way.
Component rendering and state
It is not possible to keep state in a Component when used in a loop, since it will be reused and parameters updated whenever components are being rendered, which requires state to be stored and synchronized to a long lived object (singleton, static etc.), so component state can be restored when re-rendering - sort of what have been done with AppState in the "FlightFinder" demo application, except it doesn't have any additional component state that needs to be synchronized and restored, which is what ends up causing the headache.
I have components which dynamically changes in state based on both user actions and also timers that automatically add or remove to a list or modifies the data.
Here is a simple example to set the scene:
@foreach (var msg in _list)
{
<Test Message="@msg" />
}
@functions
{
List<string> _list = new List<string>() {"Message1", "Message2"};
}
This will render 2 Test Components with the Message property set to "Message1" and "Message2":
Component1 = "Message1" - new instance, Init() called
Component2 = "Message2" - new instance, Init() called
If "Message1" is removed from the list and StateHasChanged() is called to re-render:
Component1 = "Message2" - update
Component2 - instance is disposed.
Init() will not be called on Component1 since it is being re-used and the parameter is updated.
Component2 will be disposed even though it contains "Message2" which still exists.
Now internal state of Component1 need to be restored with the state of Component2 as it is now displaying the content of what was previously shown in Component2.
Now lets say you render the list in reverse order because you want new items to show up at the top of the list instead of at the bottom. The initial list only contains "Message1". The following is rendered:
Component1 = "Message1" - new instance, Init() called
Now "Message2" is prepended to the list and StateHasChanged() is called to re-render the list:
Component1 = "Message2" - update
Component2 = "Message1" - new instance, Init() called (again for "Message1")
This means that in Init() you can't setup anything related to the data of the Component, since it will never be triggered for "Message2", and data can be switched around between components at any time.
So a Component is basically a template and cannot hold any state (maybe that's what got me confused in the first place as I was thinking of it in another way than just a template for rendering).
This has lead me to use ComponentModels (like a ViewModel) that holds the state and the initial data/model, but this also means that I now have to wrap all data/models with this ComponentModel and take care of storing the ComponentModel in a long lived object (static, singleton etc.), so state can be restored when re-rendered.
References
Another thing is that getting and handling the reference to a Component generated in a loop is somewhat difficult.
One way around it, is to create a property and let the setter handle it, but then you need to track components that is no longer rendered and which needs to be removed from the list with component references and disposed.
Example:
@foreach (var msg in _list)
{
<Test Message="@msg" Ref="@Ref" />
}
@functions
{
List<string> _list = new List<string>() {"Message1", "Message2"};
List<Test> _refs = new List<Test>();
protected Test Ref
{
get => throw new NotSupportedException("Not supported");
set => _refs.Add(value);
}
}
Alternatively you need to let the Component register itself with its parent(s), either using
CascadingValue
Reference to the parent that is supplied using a property parameter on the the component using some standard interface so it doesn't have a hard dependency on the parent/context.
Two event delegates - RefBind/OnRefBind (happens after first render since ref is not available before), and RefUnbind/OnRefUnbind (happens when disposed or if component is not rendered - but not sure how it would currently be detected).
This can of course be implemented by extending BlazorComponent, but a standardized way for handling this would be great.
Describe the solution you'd like
Component rendering and state
I'm not exactly sure how to solve the problem, and even if it should be considered one.
One way of controlling which component(s) gets instantiated, updated or disposed could be if it was possible to supply an Id/Identifier (object) to the Component which will be used to determine how it is handled:
If Id does not equal an Id of a previously rendered Component, a new instance of the Component is created.
If Id equals an Id of a previously rendered Component, the existing instance is updated with the parameters supplied.
Any Component previously rendered that is no longer used/updated is Disposed, which happens already.
References
It would be great to have some standardized way of handling it.
Let the Ref attribute have an overload that take a delegate/lambda that can be used to register reference and signal a change that causes it to be unregistered. Either state is supplied as a parameter in a Action<TComponent, State> or Action is used and Component has a property like "IsRendered" that can be checked to know if reference should be stored or removed.
Alternatively as mentioned previously you can bind to events like RefBind/OnRefBind and RefUnbind/OnRefUnbind on a Component, as an alternative to the "Ref" attribute.
Additional context
On one hand I understand what is going on and why it happens - it finally makes sense now.
But I wish there where some better way for handling both how components are created/updated and how references are retrieved / managed in order to prevent re-rendering and state handling, but if state should never be stored in the component then it of course doesn't matter.
At first thought maintaining state in the component would make life much easier.
But maybe it is not well thought true and storing state in the component is not desirable and will end up with negative side-effects.
The text was updated successfully, but these errors were encountered:
Eilon
added
the
area-mvc
Includes: MVC, Actions and Controllers, Localization, CORS, most templates
label
Jan 28, 2019
Is your feature request related to a problem? Please describe.
Related to Blazor 0.7.0 and Blazor Components.
The way rendering of Components in a loop is handled causes me some frustration and maybe it could be improved.
I am sure you have probably thought a lot about how to handle this and maybe I am going about it in the wrong way.
Component rendering and state
It is not possible to keep state in a Component when used in a loop, since it will be reused and parameters updated whenever components are being rendered, which requires state to be stored and synchronized to a long lived object (singleton, static etc.), so component state can be restored when re-rendering - sort of what have been done with AppState in the "FlightFinder" demo application, except it doesn't have any additional component state that needs to be synchronized and restored, which is what ends up causing the headache.
I have components which dynamically changes in state based on both user actions and also timers that automatically add or remove to a list or modifies the data.
Here is a simple example to set the scene:
This will render 2 Test Components with the Message property set to "Message1" and "Message2":
If "Message1" is removed from the list and StateHasChanged() is called to re-render:
Init() will not be called on Component1 since it is being re-used and the parameter is updated.
Component2 will be disposed even though it contains "Message2" which still exists.
Now internal state of Component1 need to be restored with the state of Component2 as it is now displaying the content of what was previously shown in Component2.
Now lets say you render the list in reverse order because you want new items to show up at the top of the list instead of at the bottom. The initial list only contains "Message1". The following is rendered:
Now "Message2" is prepended to the list and StateHasChanged() is called to re-render the list:
This means that in Init() you can't setup anything related to the data of the Component, since it will never be triggered for "Message2", and data can be switched around between components at any time.
So a Component is basically a template and cannot hold any state (maybe that's what got me confused in the first place as I was thinking of it in another way than just a template for rendering).
This has lead me to use ComponentModels (like a ViewModel) that holds the state and the initial data/model, but this also means that I now have to wrap all data/models with this ComponentModel and take care of storing the ComponentModel in a long lived object (static, singleton etc.), so state can be restored when re-rendered.
References
Another thing is that getting and handling the reference to a Component generated in a loop is somewhat difficult.
One way around it, is to create a property and let the setter handle it, but then you need to track components that is no longer rendered and which needs to be removed from the list with component references and disposed.
Example:
Alternatively you need to let the Component register itself with its parent(s), either using
This can of course be implemented by extending BlazorComponent, but a standardized way for handling this would be great.
Describe the solution you'd like
Component rendering and state
I'm not exactly sure how to solve the problem, and even if it should be considered one.
One way of controlling which component(s) gets instantiated, updated or disposed could be if it was possible to supply an Id/Identifier (object) to the Component which will be used to determine how it is handled:
References
It would be great to have some standardized way of handling it.
Let the Ref attribute have an overload that take a delegate/lambda that can be used to register reference and signal a change that causes it to be unregistered. Either state is supplied as a parameter in a Action<TComponent, State> or Action is used and Component has a property like "IsRendered" that can be checked to know if reference should be stored or removed.
Alternatively as mentioned previously you can bind to events like RefBind/OnRefBind and RefUnbind/OnRefUnbind on a Component, as an alternative to the "Ref" attribute.
Additional context
On one hand I understand what is going on and why it happens - it finally makes sense now.
But I wish there where some better way for handling both how components are created/updated and how references are retrieved / managed in order to prevent re-rendering and state handling, but if state should never be stored in the component then it of course doesn't matter.
At first thought maintaining state in the component would make life much easier.
But maybe it is not well thought true and storing state in the component is not desirable and will end up with negative side-effects.
The text was updated successfully, but these errors were encountered: