-
Notifications
You must be signed in to change notification settings - Fork 10.2k
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
Interactive component looses state on Navigate to same page #52356
Comments
@Nephim thanks for contacting us. That section of the docs is confusing. We'll get it fixed/sorted out. The TL;DR is:
@guardrex can you update the mentioned paragraph to make it clear that enhanced navigation and client side routing are different unrelated things? Particularly this part: And the other part that talks about the routers nesting. They don't nest. |
On the PR, @MackinnonBuck said ...
... so I asked ...
... @MackinnonBuck said ...
... but also that ...
Cross-ref: dotnet/AspNetCore.Docs#30336 I left it in (reworded) ... BTW @Nephim's post lost the code-fenced
I think that's the direction that @MackinnonBuck was leaning the first place. I'll confirm/add/update to make sure that your, @javiercn, "TL;DR" explanation is present. UPDATE@MackinnonBuck ... Let's discuss further on the PR that I opened (dotnet/AspNetCore.Docs#31118). |
My desired behavior?It sounds to me that, my desired behavior would be possible, by having a router component in web-assembly. Which would take over when the page has fully loaded and avoid the current issues with the Counter Component getting "reset", is this correctly understood?Or maybe it would be as simple as setting a I'll try out the possible solutions Monday. Regarding the docsFrom my perspective, if you mention something like this:
I feel like there should probably be a link describing or showing what this does and why it might be useful? Also an explanation about how you can avoid having your component reset would be very valuable. At least to me but I may be biased, and maybe there is a way explained somewhere in the docs but I have been unable to find any mention of it, @key docs might be how you would do it but from reading that doc its hard to see and hard to search as navigation or SSR/Webassembly isn't mentioned. |
I have now tested both possible solutions, however none of them seem to be working for me. It could be that I'm doing something wrong, so I'll showcase a Minimal project that I've been testing with, I have put it in a public GitHub. Its the Template project from VS with SSR + InteractiveAuto. @key<PageTitle>Counter</PageTitle>
<div @key=1>
@if (Count is not null)
{
<ComponentDetails Count="@Count" />
}
else
{
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
<button @onclick=@(() => NavigationManager.NavigateTo($"/wasm/counter/{currentCount}"))>SubPage</button>
}
</div>
@code {
[Parameter] public int? Count { get; set; }
[Inject] private NavigationManager NavigationManager { get; set; } = default!;
protected override void OnAfterRender(bool firstRender)
{
if(firstRender)
{
Console.WriteLine("First render!");
}
}
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
} Wasm RouterWsamRouter.razor@page "/wasm/*"
@rendermode @(new InteractiveAutoRenderMode(prerender: false))
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
<RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.WasmLayout)" />
<FocusOnNavigate RouteData="@routeData" Selector="h1" />
</Found>
</Router>
@code {
protected override void OnInitialized()
{
base.OnInitialized();
Console.WriteLine("Using WASM router");
}
}
Counter.razor@page "/wasm/counter/{Count:int?}"
@rendermode InteractiveAuto
<PageTitle>Counter</PageTitle>
<div @key=1>
@if (Count is not null)
{
<ComponentDetails Count="@Count" />
}
else
{
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
<button @onclick=@(() => NavigationManager.NavigateTo($"/wasm/counter/{currentCount}"))>SubPage</button>
}
</div>
@code {
[Parameter] public int? Count { get; set; }
[Inject] private NavigationManager NavigationManager { get; set; } = default!;
protected override void OnAfterRender(bool firstRender)
{
if(firstRender)
{
Console.WriteLine("First render!");
}
}
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
}
Please let me know if there is anything else I should test |
I have figured out a solution that works for me. It feels a little hack'y, but seems the best solution I've found so far. I'm basically using a nested router to intercept navigation events and keep them in Interactive Auto Render mode. Wasm Router@page "/counter/{Count:int?}"
@rendermode InteractiveAuto
<Router AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
@{
routeData.RouteValues.TryGetValue("Count", out var count);
}
<Counter Count=@((int?)count) />
</Found>
</Router>
@code {
[Parameter] public int? Count { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
Console.WriteLine("Using WASM router");
}
protected override void OnAfterRender(bool firstRender)
{
Console.WriteLine("Render in Router");
}
} Wasm Router@rendermode InteractiveAuto
<PageTitle>Counter</PageTitle>
@if (Count is not null)
{
<ComponentDetails Count="@Count" />
}
else
{
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
<button @onclick=@(() => NavigationManager.NavigateTo($"/counter/{currentCount}"))>SubPage</button>
}
@code {
[Parameter] public int? Count { get; set; }
[Inject] private NavigationManager NavigationManager { get; set; } = default!;
protected override void OnAfterRender(bool firstRender)
{
Console.WriteLine("Render");
if(firstRender)
{
Console.WriteLine("First render!");
}
}
private int currentCount = 0;
private void IncrementCount()
{
currentCount++;
}
} The Counter page now only gets SSR loaded once and then WASM/Server takes over, keeping the state. Maybe you find my solution interesting. Please let me know if you have any questions. I've put the complete solution on GitHub. Edit...I've discovered an issue with my approach basically the router would be unable to navigate away from the internal Counter component when it had first been rendered. I have solved this in a not so pretty way.Wasm Router@page "/counter/{Count:int?}"
@rendermode InteractiveAuto
<Router OnNavigateAsync="Navigation" AppAssembly="@typeof(Program).Assembly">
<Found Context="routeData">
@{
routeData.RouteValues.TryGetValue("Count", out var count);
}
<Counter Count=@((int?)count) />
</Found>
</Router>
@code {
[Parameter] public int? Count { get; set; }
[Inject] NavigationManager NavigationManager { get; set; } = default!;
private void Navigation(NavigationContext context)
{
if (!context.Path.StartsWith("counter", StringComparison.InvariantCultureIgnoreCase))
{
NavigationManager.NavigateTo(context.Path, true);
}
}
protected override void OnInitialized()
{
base.OnInitialized();
Console.WriteLine("Using WASM router");
}
protected override void OnAfterRender(bool firstRender)
{
Console.WriteLine("Render in Router");
}
}
Do you know a better way of achieving this? Essentially the router should propagate the navigation if the page Isn't "counter". Instead of just reloading the page. |
@Nephim another way you can do this is by moving most of the page's functionality into a non-page component, making the page non-interactive, and specifying an interactive render mode for the factored-out component. For example: CounterPage.razor @page "/counter/{count:int}"
@inject NavigationManager NavigationManager
<PageTitle>Counter</PageTitle>
<h1>Counter</h1>
<Counter @key="0" @rendermode="InteractiveServer" Count="Count" />
@code {
[Parameter] public int Count { get; set; }
} This example specifies a constant value for Does that approach work for you? |
@MackinnonBuck I have just tested this on the example solution and looks like it solves my Issue, and It's a very clean and simple solution to boot. I'll test it with a more expansive solution tomorrow but I expect it should work perfectly in my case. From my point of view this information is very useful and I have not been able to find it, anywhere else. So maybe you should consider updating the docs with a paragraf mentioning this. Maybe as a replacement for this commit. It could say something like:
Maybe with a code snip like this: @page "/counter/{count:int?}"
<PageTitle>Counter</PageTitle>
<Counter @key="0" @rendermode="InteractiveServer" Count="Count" />
@code {
[Parameter] public int? Count { get; set; }
} I'll report back if it worked for me, please let me know if there I can help with something. 😄 UpdateIt also works on the expansive solution and it does what I want and acts the way I would expect. So everything looking good now 👍 |
@Nephim ... We've decided to hold off on it for the moment, but we have the content ready to include. I'm going to add a note to one of my tracking issues and chat further with Mackinnon about it in 24Q1. |
Thank you for contacting us. Due to a lack of activity on this discussion issue we're closing it in an effort to keep our backlog clean. If you believe there is a concern related to the ASP.NET Core framework, which hasn't been addressed yet, please file a new issue. This issue will be locked after 30 more days of inactivity. If you still wish to discuss this subject after then, please create a new issue! |
Is there an existing issue for this?
Describe the bug
This might be the same/related to Issue #51584.
I'm having issues refactoring an application going from Blazor server to the new SSR + InteractiveAuto mode.
I have a main page where i fetch all the data from my backend in the OnAfterRender if its firstRender.
This page then has a button that navigates to the same page but with different parameters. All of this works.
However on Blazor server the state of the component was kept and only re-rendered with new parameters.
Counter Component
ComponentDetails
In the new Dotnet 8 Rendering mode the Component is "thrown away" and a whole new component is rendered which then has to fetch data from the backend all over again.
Initial Load:
![image](https://private-user-images.githubusercontent.com/39805658/285503102-292e63ca-f86c-400b-97dd-1536d588b68d.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk0NTQ1NjQsIm5iZiI6MTczOTQ1NDI2NCwicGF0aCI6Ii8zOTgwNTY1OC8yODU1MDMxMDItMjkyZTYzY2EtZjg2Yy00MDBiLTk3ZGQtMTUzNmQ1ODhiNjhkLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjEzVDEzNDQyNFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWE4ZTAyMzQ2ZGE3MGJhZTJmODQ3ZjljOTRmYmNhYWYxMmE2OGVlYmNjYjA2M2MyMjI1MWQ2YjFlZjhkYzIzNGQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.qZmPa8X4WNGK4z88cgknnQHwHGORGK8WpUN1O6z3hqk)
Navigation:
![image](https://private-user-images.githubusercontent.com/39805658/285503042-2cdcc4ab-7f9e-4de6-8e5b-61f4ab8da14f.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Mzk0NTQ1NjQsIm5iZiI6MTczOTQ1NDI2NCwicGF0aCI6Ii8zOTgwNTY1OC8yODU1MDMwNDItMmNkY2M0YWItN2Y5ZS00ZGU2LThlNWItNjFmNGFiOGRhMTRmLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNTAyMTMlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjUwMjEzVDEzNDQyNFomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWYzN2IzMTdjNmExNDYwNjA2NTY2NjY3ZGZjYTRmMTgzNWQyNGMyNDI2ZmZjZDlkNjNkN2IyZGMxMTY5MDlmYmQmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0In0.zQCr1K7bVpiMIkhs0ZOladRLGqZT_U8wGQhQrDmtZ5U)
This might not be a bug but I've been unable to find a way to handle this. The docs mention something interesting but I haven't been able to find how such a thing would work.
Enhanced navigation and form handling
"If enhanced navigation is available for interactive client routing, navigation always goes through the interactive client-side router. This point is only relevant in an uncommon scenario where an interactive is nested inside a server-side rendered . In that case, the interactive router takes priority when handling navigation, so enhanced navigation isn't used for navigation because it's a server-side routing feature."
Expected Behavior
The navigation should not fetch a new page and should just re-render the current component with new parameters.
.NET Version
Dotnet 8
The text was updated successfully, but these errors were encountered: