Question about reactivity in Blazor
Hi, I'm working on a Blazor project. I have some experience with C# in general and professionally I work with Angular. Specifically Angular recently tackled a big issue with reactivity with Signals. Before that I often used RxJS, and in .Net a similar implementation of RxJS is System.Reactive.
In Blazor I do not fully understand how the reactivity works out of the box, without using any additional libraries. The goal of the project is to be able to parse replays of the game Supreme Commander: Forged Alliance and to show various information about the replay on different pages. Just like in Angular, one can use a service to deal with data that needs to exist across pages. To set that up, in program.cs I have the following snippet to make sure the replay service is a singleton:
The replay service itself is quite simple, just a C# class that uses a library to load in the replay from a binary blob:
Now, in a Blazor page I can do the following and that appears to work just fine:
However, in the navigational menu I want to hide some items until there is a replay in the service. It won't make a lot of sense to show the build orders tab when there is no replay. So I wrote it as such:
Now back to reactivity - I noticed that the page updates immediately once the replay is loaded. All good there. But the navigational menu is only updated after interacting with it in some fashion, such as by clicking on it. That's of course not what I want - once a replay is available, it should update automatically.
Secretly I was hoping that Blazor was as reactive as the Angular Signals, without the boilerplate of an RxJS-like library. But the current behavior is not sufficient. Where can I read up about reactivity in Blazor, and in general, why is the navigational menu not updating in my example? And should I still use a reactive library? Or am I just making a rookie mistake? 😄
GitHub
Build software better, together
GitHub is where people build software. More than 100 million people use GitHub to discover, fork, and contribute to over 420 million projects.
14 Replies
The problem is, that your parent component / layout cannot know that your state has changed.
A simple but heavy handed approach would be to call StateHasChanged() after loading the file inside your page code. But there are nearly always better solutions.
I suspect establishing an CascadingParameter wrapping your entire app instead of using a service may work.
Are you referring to this:
- https://learn.microsoft.com/en-us/aspnet/core/blazor/components/cascading-values-and-parameters
ASP.NET Core Blazor cascading values and parameters
Learn how to flow data from an ancestor Razor component to descendent components.
Yes, but thats just a guess.
I'm trying to understand it, thank you for your time. I'm reading up on this:
- https://chrissainty.com/understanding-cascading-values-and-cascading-parameters/
I think cascading values work between components that are in a hierarchy. But my service is injected and therefore not part of the hierarchy. I'm trying to apply it there too, but it's not quite working out.
You mentioned that
StateHasChanged
is a heavy-handed approach, what makes you say that?Chris Sainty - Building with Blazor
Understanding Cascading Values & Cascading Parameters
In this post I provide an overview of cascading values and cascading parameters, what they are, how you can use them and some potential drawbacks.
Oh wait, maybe I missunderstood the cascading parameters. Let me re-try.
Maybe this is what I need:
- https://learn.microsoft.com/en-us/aspnet/core/blazor/components/cascading-values-and-parameters?view=aspnetcore-8.0#root-level-cascading-values
But work is calling, I'll look at it later today again 🙂
ASP.NET Core Blazor cascading values and parameters
Learn how to flow data from an ancestor Razor component to descendent components.
There's also this:
- https://learn.microsoft.com/en-us/aspnet/core/blazor/state-management?view=aspnetcore-8.0&pivots=server#in-memory-state-container-service
ASP.NET Core Blazor state management
Learn how to persist user data (state) in Blazor apps.
Pharaz Fadaei
Automatic Component State Management in Blazor using INotifyPropert...
In Blazor, changes to the state of a component become visible to the user when the component re-renders. Blazor has some conventions to make a component automatically re-render when it thinks there is a chance for the component's state to be changed…
https://jonhilton.net/blazor-rendering/
Maybe this is of interest.
Tbh I may just misremember an article I have read some time ago, about StateHasChanged being quite expensive.
Need to read more about ist myself.
State Hasn't Changed? Why and when Blazor components re-render
Sure you could just keep throwing StateHasChanged calls at your component until it finally re-renders, but what's really going on behind the scenes?
Article is book marked, I'm also looking at:
- https://www.youtube.com/watch?v=EUOimtP78jQ
dotnet
YouTube
On .NET Live - Building Reactive UIs with Blazor
In this live session, Rodney Littles II shows us how to build Blazor applications with the ReactiveUI framework
Featuring: Rodney Littles II (@rlittlesii), Cecil Phillip (@cecilphillip)
Get your questions answered on the Microsoft Q&A for .NET - https://aka.ms/dotnetqa
​
Learn .NET with free self-guided learning from Microsoft Learn: http://ak...
This article is excellent and it helped a lot with understanding change detection
I also understand now why you mentioned calling
StateHasChanged
manually, since that is what happens in the background
I also now better understand why this library exists:
- https://github.com/phorks/phork-blazor-reactivity/
And in action:
- https://github.com/phorks/phork-blazor-reactivity/blob/main/docs/IN-ACTION.md
I do feel this perhaps should've been part of Blazor to begin with 🤔 , it requires a lot of boiler plate to have global state in the current default setup
I've implemented the Phork library, see also:
- https://github.com/Garanas/scfa-cs-replay/pull/3/commits/69330fb632854a457b1481c2c06078680f44cb87
Using the getting started guide (the readme) and the action guide of the repository. It now works as I'd expect it to and the syntax is relatively minimal
Thank you @Joschi for taking the time for this little journey here, I'd like to credit you in the readme. Is there a name that you'd like me to use, or shall I go with your Discord tag? Assuming here that you'd like to be mentioned 🙂Thanks for offering.
I don't think that you have to credit me for writing a few tidbits and throwing you a link.
But if you insist feel free to link my github account. https://github.com/JoschiZ
I've mentioned you in the credits section of the readme: https://github.com/Garanas/scfa-cs-replay
I may update to make it look better in the future 🙂
I'll be keeping track of various references that I found useful on the Wiki:
- https://github.com/Garanas/scfa-cs-replay/wiki/Blazor
And the page exists and is 'functional':
- https://garanas.github.io/scfa-cs-replay/
Where 'functional' means that all the complicated unknowns are resolved, and now it's about filling in the details 😄
GitHub
GitHub - Garanas/scfa-cs-replay: A library to read and interpret re...
A library to read and interpret replay files of Supreme Commander: Forged Alliance (Forever) - Garanas/scfa-cs-replay
GitHub
Blazor
A library to read and interpret replay files of Supreme Commander: Forged Alliance (Forever) - Garanas/scfa-cs-replay
Thanks again!