My app is in VS2022 C# Blazor WASM-hosted with .Net 6. I have a blazor page with a sub-Component that has functionality to allow the PAGE to see a select-box for Active/Inactive (two options) and a select-box for a list of Customer names (abbreviated).
The PAGE looks like this:
The TOP rectangle is the sub-component that "acts" like a "function" that returns (via CALLBACK) the selected "CustomerID" (shown is 'ARCTB'). The sub-component reacts to the "select-choice" (@onchange=) by CALLBACK-parameter to the parent form with the 'selected' CustomerID so the parent-page can fetch the vehicles for that selected customer.
On the initial sub-component OnParametersSetAsync(), where the list of customers and first CustomerID are determined, the CALLBACK to the PAGE is made using the List[0] element for CustomerID. At this point at the PAGE, a re-render takes place for both the PAGE and the sub-component. That causes the sub-component's OnParametersSetAsync() to fire again and the CALLBACK, again for the infinite loop.
The SECOND rectangle is part of the PAGE-html and allows the user to select active or inactive vehicles, that filters the list of customer vehicles to that of _listOfAllVehicles( v => v.IS_ACTIVE == true-or-false)
.
The problem of infinite-loop occurs because the CALLBACK to the PAGE forces re-renders and then the sub-component performs another CALLBACK from the OnParametersSetAsync() method. This is the infinite-loop problem I seek an answer.
Being new to the latest Blazor, I am not sure if ShouldRender() could be used on the PAGE or sub-component or both? Maybe using a sub-component "flag" to denote "OkToCallback" to limit repeated CALLBACK calls.
Your comments, questions and answers are welcome.
As requested by @Marius, the sub-component code is shown below. I have included a TEST for '_IsLoading' that STOPPED the infinite-loop.
Thank you @Marius for the ANSWER. :-)
protected override async Task OnParametersSetAsync() {
// Retain the Parameters.
_LoggedInCustomerName = pLoggedInCustomerName;
_LoggedInUserRoleName = pLoggedInRole;
_IsUserNotFCI = (_LoggedInUserRoleName != "FCI");
_IsActiveCustomers = (_ddlActive_Value == 1);
await Task.Delay(10);
_ddlCustomer_Value = RefreshCustomerList(_ddlActive_Value);
if (_ddlCustomer_Value > 0) {
// Set to first-option (index) (refer to wwwroot\index.html -- see OnAfterRender()
if (_IsLoading) {
// Update the selected customer.
ChangeEventArgs evntArgs = new ChangeEventArgs();
evntArgs.Value = _ddlCustomer_Value.ToString();
await SelectedCustomerChanged(evntArgs);
_IsLoading = false;
}
}
base.OnParametersSet();
}
private int RefreshCustomerList(int pCustomersActiveFlag) {
// Refresh the customer-list based on the 'ddlActiveFilter' ACTIVE-STATUS and USER-ROLE.
// Return the zero-th element's UID_CUSTOMER of the list of customers.
bool isActiveFlag = (pCustomersActiveFlag==1);
int retValue = 0;
if (_ListAllCustomers != null) {
// When LOGGEDIN-USER is NOT FCI, reduce the list to ONLY the loggedin-user customer.
if (_IsUserNotFCI) {
// This returns a list of a single <Customer> record.
_ListFilteredCustomers = _ListAllCustomers.Where(c => (c.TXT_CUSTOMER_NAME == _LoggedInCustomerName)).ToList();
} else {
// This returns a list of all ACTIVE or INACTIVE customers based on 'pCustomersActiveFlag' => 'isActiveFlag'.
_ListFilteredCustomers = _ListAllCustomers.Where(c => (c.BOOL_IS_CUST_ACTIVE == isActiveFlag)).OrderBy(r => r.TXT_CUSTOMER_ABBREV).ToList();
}
// Set the 'ddlCustomer_Value' to the UID of the first-customer in the list.
retValue = _ListFilteredCustomers[0].UID_CUSTOMER;
}
return retValue;
}
private async Task SelectedCustomerChanged(ChangeEventArgs e) { // This is the event after selection is made.
int iArgValue = Convert.ToInt32(e.Value);
_ddlCustomer_Value = Convert.ToInt32(e.Value);
Customer customer = _ListFilteredCustomers!.Where(rec => rec.UID_CUSTOMER == _ddlCustomer_Value).FirstOrDefault();
if (customer != null) {
_SelectedCustUID = customer.UID_CUSTOMER;
_SelectedCustName = customer.TXT_CUSTOMER_NAME;
}
// Callback to Parent.
await OnCustomerSelection.InvokeAsync(_ddlCustomer_Value);
}
OnParametersSetAsync
and OnParametersSet
will fire whenever a parameter changes, even if the value is the same as before. You can write some code to avoid this.
Example: Is previous parameter value the same as current ? skip load : load
private string _cachedValue;
[Parameter]
public string parameterValue { get; set; }
protected override async Task OnParametersSetAsync()
{
if (parameterValue != _cachedValue)
{
_cachedValue = parameterValue;
LoadComponent();
}
}
private void LoadComponent() {
//...
}
The above is just an example of one method of doing this. Use logic that suits your code.