1

I'd like to reload a Blazor page component without triggering a full browser reload. Using NavigationManager to reload works, but I'm looking for a softer approach:

  • The page itself stays loaded, so singleton services keep their state.
  • The component (for example, one with @page "/entity/{id:int}/edit") should call IDisposable.Dispose if implemented.
  • A new instance of that component is created, so OnInitializedAsync runs again and all markup is rebuilt.

I think a workaround is to navigate to a "middle" page like /soft-reload?returnUrl=... and redirect back, but I wonder if there's a better way? Also, would the same technique work for non-page components?

Ideally, without involving JavaScript.

3
  • 1
    That begs the question: Why? OnParametersSet() can do everything OnInitialized() can and requiring Dispose() in your logic is a big code smell. Commented Nov 6 at 7:35
  • @HenkHolterman one example is that my (Web) component does not change the value based on @bind-Value/[value] (just like normal input but web component doesn't get @bind="" support) and changing the value is a big pain with JS interop that I want to avoid, so getting a fresh component is much easier. Also Dispose is just an example to demonstrate a normal component's lifecycle, my current code does not actually use it. Commented Nov 6 at 9:18
  • Not clear what the bind problems are. But I'll post an answer. Commented Nov 6 at 10:19

3 Answers 3

1

When you add this to the template Counter page:

<MyComp @key="@currentCount" Value="someValue" /> 

then MyComp will be disposed and recreated as a new instance on every render (buttonclick).

Sign up to request clarification or add additional context in comments.

Comments

1

This is my current approach using the "middle" page as mentioned:

  1. Write an extension method for NavigationManager:
public static class ClientUtils { public static void SoftReload(this NavigationManager Nav) { Nav.NavigateTo("/soft-reload?returnUrl=" + Uri.EscapeDataString(Nav.Uri), false); } } 
  1. Write a handler SoftReload.razor:
@page "/soft-reload" @inject NavigationManager Nav; <Spinner /> @code { [Parameter, SupplyParameterFromQuery(Name = "returnUrl")] public string ReturnUrl { get; set; } = "/"; protected override void OnInitialized() { Nav.NavigateTo(ReturnUrl); } } 
  1. Usage: simply call SoftReload():
 async Task EditAsync() { DisableUI(); await ProcessAsync(); Nav.SoftReload(); // Nav is an injected NavigationManager } public void Dispose() { Console.WriteLine("Edit page disposed"); } 

So far it seems to work pretty well for me: fresh data shows up, the test Dispose is printed out. Obviously I can only reload a whole component page with this, not just a single component.

Comments

0

The way to destroy and re-create a component at runtime is to remove it from the RenderTree and the simplest way to do this is to use a @key directive that you willfully change.

Here is a basic ComponentReloader.razor that can be used either for @page components (by using it in MainLayout around the @Body)

<CascadingValue IsFixed=true Name=@Ident Value=ReloadChild> <div @key=childKey style="display:contents"> @ChildContent @if (ShowButton) { if (!ShowButtonInline) { <br /> } <button class="m-2 btn btn-danger" @onclick="ReloadChild">@ButtonTitle</button> } </div> </CascadingValue> 
@code { [Parameter] public string Ident { get; set; } = "PageReload"; [Parameter] public required RenderFragment ChildContent { get; set; } = b => { }; [Parameter] public bool ShowButton { get; set; } = false; [Parameter] public bool ShowButtonInline { get; set; } = false; [Parameter] public string ButtonTitle { get; set; } = "Reload"; string childKey = Guid.NewGuid().ToString("d"); void ReloadChild() => InvokeAsync(() => { childKey = Guid.NewGuid().ToString("d"); StateHasChanged(); }); } 

Of course, this is just a quick sample - you can adapt it as needed.

Note: style=display:contents ensures the div we are using to reload the component does not render on the page - just its contents get rendered.

Note: When the ReloadChild action is invoked (from its own button or called by the child component) it changes the childKey and re-renders - which causes Blazor to dispose (if applicable) of the components in its RenderTree and re-create them.

Example for MainLayout that renders a "Reload Page" button on every page:

<ComponentReloader Ident="ReloadPage" ShowButton=true ButtonTitle="Reload Page"> @Body </ComponentReloader> 

Sample page with automatic reload button

Example for a counter component that wants to render its own button (or some other trigger):

<ComponentReloader Ident="ReloadCounter"><Counter/></ComponentReloader> 

And in the counter you can get a reference to the ComponentReloader:

[CascadingParameter(Name = "ReloadCounter")] public Action? ComponentReload { get; set; } 

Then it is just a matter of deciding how/when to call the ComponentReload action.

public void Reload() => ComponentReload?.Invoke(); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.