Reputation: 9143
Look at this very basic component:
<div>
@param1
<button @onclick="@btn_on_click">Cliquez là</button>
</div>
@code
{
[Parameter]
public int param1 { get; set; }
[Parameter]
public Action<int> on_evt_test_fire { get; set; }
void btn_on_click()
{
param1 += 1;
this.on_evt_test_fire(param1);
}
}
And look at this very basic Page:
@message
<button @onclick="@on_btn_click">Click me</button>
<MyComponent param1="1" on_evt_test_fire="@on_evt" />
<MyComponent param1="2" on_evt_test_fire="@on_evt" />
@code
{
private String message = "";
private void on_evt(int param_evt)
{
message = "Button clicked inside component";
StateHasChanged();
}
private async Task on_btn_click()
{
message = "Button clicked in this page";
}
}
I have a problem with StateHasChanged().
My first question is: Why should i call StateHasChanged in on_evt whereas it is not necessary in on_btn_click. The only difference between this 2 methods is the first is called from inside the component. Why should i call StateHasChanged in this specific case ?
When i call StateHasChanged(), the 2 components are reset: They take their initial values...
Thanks for your help
Upvotes: 1
Views: 884
Reputation: 45684
whereas it is not necessary in on_btn_click
It is not necessary on on_btn_click because the StateHasChanged method is automatically called on UI events.
[Parameter]
public Action<int> on_evt_test_fire { get; set; }
You shouldn't use the Action delegate. Use EventCallback 'delegate' instead, like this:
Note: When you use the Action delegate the target of the event is the current component ( child component), but when you use EventCallback, the target is the parent component, which is why you don't need to add the call to the StateHasChanged method. Before the EventCallback was discovered, we had to call the StateHasChanged method. It was ages ago...
[Parameter]
public EventCallback<int> on_evt_test_fire { get; set; }
And you should call it like this:
public async Task btn_on_click()
{
if( on_evt_test_fire.HasDelegate)
{
temp++;
await on_evt_test_fire.InvokeAsync(temp);
}
}
You shouldn't use the param1 parameter to increment its value. Define a new variable and assigned it the value of param1 in the OnInitialized method like this:
protected override void OnInitialized()
{
temp = param1;
}
When i call StateHasChanged(), the 2 components are reset: They take their initial values..
--
Don't create components that write to their own parameter properties Parameters are overwritten under the following conditions:
A child component's content is rendered with a RenderFragment.
StateHasChanged is called in the parent component. Parameters are reset because the parent component rerenders when StateHasChanged is called and new parameter values are supplied to the child component.
Upvotes: 2
Reputation: 3073
The answers from both @Henk and @SilenceAmongCrows are correct. One more thing to add is that in your child component, if you use EventCallBack<T>
instead of Action<T>
and have your param1
property in your child component bound to a property in your @code block of the parent component, the Blazor engine will do a much better job of tracking what to refresh and when to do so.
Upvotes: 0
Reputation: 363
I believe the @onClick callback forces a child re-render due to some hidden logic. Your callback or event when assign to the button's @onClick is most likely being added to a list of things that onClick does internally. Take this with a grain of salt, this is just my theory.
Edit: your parameters being reset are because of the setState() forcing your parent to re-render from the ground up with the hard coded "1" and "2". When the event triggers for the onbuttonclicked, I believe it sets the state internally within the components, thus retaining data. More salt.
Upvotes: 0