Open carlfranklin opened 7 months ago
@carlfranklin thanks for reaching out.
This is likely because you are trying to pass the cascading parameters across serialization boundaries (which is not generally supported)
There's a bug tracking support for this type of thing here, however, in general the values will have to be serializable https://github.com/dotnet/aspnetcore/issues/51969
I think the problem here is that Routes.razor which contains the CascadingState is not marked as Interactive, so it does not participate in the interactivity.
If you mark Routes as Interactive, this problem goes away.
I guess you could also move the CascadingState to a new layout that is marked Interactive and have any Interactive routable pages use the new layout.
There may be other ways of course, but it doesn't seem like a bug that state parented in a non-interactive component is null.
If you mark Routes as Interactive, isn't that effectively Global mode?
The goal is to have state accessible even when switching between Server and Wasm render modes, and even if you go to a SSR page and back.
It may not be a bug, but it means using per page/component mode (not Global) means giving up Cascading values, and therefore giving up state.
On Wed, Jan 31, 2024 at 6:57 AM SQL-MisterMagoo @.***> wrote:
I think the problem here is that Routes.razor which contains the CascadingState is not marked as Interactive, so it does not participate in the interactivity.
If you mark Routes as Interactive, this problem goes away.
I guess you could also move the CascadingState to a new layout that is marked Interactive and have any Interactive routable pages use the new layout.
There may be other ways of course, but it doesn't seem like a bug that state parented in a non-interactive component is null.
— Reply to this email directly, view it on GitHub https://github.com/dotnet/aspnetcore/issues/53482#issuecomment-1918963947, or unsubscribe https://github.com/notifications/unsubscribe-auth/AALK4DC46UQAF7FZ2GYOAPLYRIWSZAVCNFSM6AAAAABCCALPIWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMJYHE3DGOJUG4 . You are receiving this because you were mentioned.Message ID: @.***>
Yes, making routes interactive is the same effectively as global, so you would push that down to a location where it is needed. If you don't want everything in routes to have access to the state, move the state down to where it is needed. If you do want everything in routes to have access to state then it needs to be interactive.
On Wed, 31 Jan 2024, 14:39 Carl Franklin, @.***> wrote:
If you mark Routes as Interactive, isn't that effectively Global mode?
The goal is to have state accessible even when switching between Server and Wasm render modes, and even if you go to a SSR page and back.
It may not be a bug, but it means using per page/component mode (not Global) means giving up Cascading values, and therefore giving up state.
On Wed, Jan 31, 2024 at 6:57 AM SQL-MisterMagoo @.***> wrote:
I think the problem here is that Routes.razor which contains the CascadingState is not marked as Interactive, so it does not participate in the interactivity.
If you mark Routes as Interactive, this problem goes away.
I guess you could also move the CascadingState to a new layout that is marked Interactive and have any Interactive routable pages use the new layout.
There may be other ways of course, but it doesn't seem like a bug that state parented in a non-interactive component is null.
— Reply to this email directly, view it on GitHub < https://github.com/dotnet/aspnetcore/issues/53482#issuecomment-1918963947>,
or unsubscribe < https://github.com/notifications/unsubscribe-auth/AALK4DC46UQAF7FZ2GYOAPLYRIWSZAVCNFSM6AAAAABCCALPIWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMJYHE3DGOJUG4>
. You are receiving this because you were mentioned.Message ID: @.***>
— Reply to this email directly, view it on GitHub https://github.com/dotnet/aspnetcore/issues/53482#issuecomment-1919236750, or unsubscribe https://github.com/notifications/unsubscribe-auth/ACWQFPVDVTXFHVWJMNJ6Z4DYRJJRTAVCNFSM6AAAAABCCALPIWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMYTSMJZGIZTMNZVGA . You are receiving this because you commented.Message ID: @.***>
There are a lot of limitations dealing with render mode boundaries... I documented a bunch of them here (including the CascadingParameter behavior):
https://www.linkedin.com/posts/shaunbrucewalker_blazor-dotnet8-activity-7128497980755603456-QsnX
Interactive render modes can be used from a Static component, including using different render modes for sibling components.
You can't switch to a different Interactive render mode in a child component.
Parameters passed to an Interactive child component from a Static parent must be JSON serializable. This means that you can't pass render fragments or child content from a Static parent component to an Interactive child component.
CascadingParameters cannot be passed to an Interactive child component from a Static parent. The CascadingParameter will be null in the child component.
Scoped Services which contain state cannot be passed to an Interactive component. The Scoped Service state will be null.
DynamicComponent cannot be used to transition from a Static parent to an Interactive child. This is because it relies on a Type parameter which is not serializable.
Static components do not support OnAfterRender life-cycle events and do not support JS Interop
Parking in Preview 7 to provide a sample demonstrating how to do this.
@mkArtakMSFT hopefully the ultimate resolution allows bi-directional state management - which is to say that if I change a value in a wasm page, that value should continue to be consistent in server static and server interactive pages, and visa versa.
Basically, if I implement a Counter page and put the counter value in some cascading/global location, I should get the same Counter value in every render mode throughout the lifetime of a single app "session".
I think the confusion that MS created with cascading parameters in conjunction with static SSR and interactive server-side rendering was a major mistake. As far as I can see, the cascading parameters are now completely useless. I'm seriously considering ditching Blazor and using Flutter.
@Otto404 you may find the following example useful:
https://www.oqtane.org/blog/!/91/passing-state-to-components-using-ssr-in-net-8
It demonstrates a simple approach for using Cascading Parameters and Scoped Services in Blazor in .NET 8 when using static and interactive components. Note that it only works for the most common scenario I have seen in Blazor, where you are using Cascading Parameters and Scoped Services as essentially a "read-only immutable cache" for the current user session.
I think the confusion that MS created with cascading parameters in conjunction with static SSR and interactive server-side rendering was a major mistake. As far as I can see, the cascading parameters are now completely useless. I'm seriously considering ditching Blazor and using Flutter.
Before leaving Blazor, I'd look at the solutions from myself and @sbwalker - I think we've come up with good answers to the challenge that make the new render modes work really well in a lot of scenarios. And the flexible render modes are (imo) very useful in real-world scenarios.
@rockfordlhotka could you give me a pointer to your solution? thanks,
@Otto404
@rockfordlhotka could you give me a pointer to your solution? thanks,
I solved the issue without cascading parameters, instead basically creating a per-user "session" concept that is read-write across static, server-interactive, and wasm-interactive pages.
@Otto404 you may find the following example useful:
https://github.com/sbwalker/Oqtane.State
It demonstrates a simple approach for using Cascading Parameters and Scoped Services in Blazor in .NET 8 when using static and interactive components. Note that it only works for the most common scenario I have seen in Blazor, where you are using Cascading Parameters and Scoped Services as essentially a "read-only immutable cache" for the current user session.
I've successfully implemented this approach and love it. It's super simple to setup and requires no additional dependencies.
@richardaubin I am glad you found the approach to be useful... it relies on the standard Blazor primitives to move state across render mode boundaries. However there is one caveat that I ran into recently which I should mention...
When Blazor passes parameters from a static component to an interactive component, it uses serialization. This is mentioned in the official documentation: https://learn.microsoft.com/en-us/aspnet/core/blazor/components/cascading-values-and-parameters?view=aspnetcore-8.0#cascading-valuesparameters-and-render-mode-boundaries. However it does not explain what actually happens under the covers.
The reality is that Blazor serializes and encrypts the parameters and injects them into the page output, where it then decrypts and deserializes them. Basically this is similar to "ViewState" and you can view the element in your page output ie.
<!--Blazor:{"type":"server","key":{"locationHash":"8FA5FA034079DC81B5EF9C4AE6EE08D8845F2721938D620E5FBD646F9273DC08:5","formattedComponentKey":""},"sequence":0,"descriptor":"CfDJ8JwRBrGNvOJEo4T2ARppP7N0umT9coWO/IGL8lA1QlaQDJER8="}-->
Note that serialization and encryption are NOT very efficient so you have to be aware of the size of the objects you are passing as parameters, or you may face some performance issues.
Recently I was investigating a Blazor performance issue and noticed that the response size of a page request was exceptionally large:
It turned out I was passing a collection as a parameter from a static component to an interactive component. The collection contained 200 fairly complex objects. After serialization/encryption it added almost 4 MB of content to the page!
It turns out that the default serialization/encryption approach in Blazor can handle parameters up to ~100KB in size fairly well... but once you go beyond that, you need to do some performance tuning. In my case the solution was to trim the state in the collection to include only the essential properties. Another potential solution would be to use a service to store the state elsewhere (ie. on the server in a cache if using Blazor Server, or in local storage if using WebAssembly)... but that obviously requires a lot more infrastructure and complexity (ie. see the solution above from @rockfordlhotka ).
@sbwalker Thanks for the update and extra information on this method.
Currently my use case is to populate the options of an ecommerce product details page, where the options is an interactive component allowing the product details page to update with a new product when the user selects a variant option. So I'm passing a view model for the component only, hence it's very lightweight.
I also experienced an unrelated issue that caused me to restructure my code a bit to make this work.
The objects I was serializing had a property with a base class type. The property values were derived type instances.
class ProductOptions
{
public OptionBase Option { get; set; }
}
class ImageSwatchOption : OptionBase
{
public string ImageSource { get; set; }
}
new ProductOptions
{
Option = new ImageSwatchOtpion();
}
When the dynamic component pre-rendered everything passed. But on the second render, the properties deserialized to the base type, which caused downstream logic to fail because the base type didn't contain the properties of the derived types. In the code example above, the ImageSource property of the ImageSwatchOption is discarded.
This analysis leads me to wonder if disabling pre-rendering on the interactive component is possible and if it would resolve the issue.
@richardaubin I ran into that scenario as well. The way I solved it was to create a custom DTO class which I populated with the required state values to transfer across the render mode boundary. The approach is also helpful if you need to trim the state to a smaller size for optimizing performance.
Is there an existing issue for this?
Describe the bug
Using per page/component render modes, cascading parameters turn null after rendering.
Expected Behavior
The issue is documented with code at https://github.com/carlfranklin/Blazor8Test
Using per page/component render modes, cascading parameters turn null after rendering.
I have a .NET 8 Blazor Web App with the Interactive Render Mode set to Server, and the Interactivity Location set to Global.
This is my version info:
In App.razor, I have disabled pre-rendering, but it also fails with pre-rendering on.
I have a component in the client project called CascadingAppState.razor:
I have modified Routes.razor as follows:
I have implemented
CascadingAppState
in Counter.razor:Note that I am also showing the current render mode: SSR, Server, or WASM.
Behavior
Run the app and navigate to the Counter page. It works as advertised
Increment the counter, navigate to Home and back. The value persists because of the Cascading App State:
Now remove the "Global" feature in App.razor:
And add this line on line 2 of Counter.razor:
Run it again
Upon navigation, you get a Null Reference Exception on AppState:
Steps To Reproduce
Follow the instructions I have laid out
Exceptions (if any)
No response
.NET Version
Version info is included in description
Anything else?
No response