I'm a massive fan of the Redux pattern. I would say one thing, though, it's not everyone's cup of tea. It adds extra boilerplate code and may look overwhelming at first. But you get used to it and start to appreciate its power once your application grows to be a giant where state management becomes a nightmare.
Talking of sidekicks, React has Redux; Angular has NGRX; what does Blazor have? I'm really into this library called Fluxor recently. You can carry over your existing knowledge of any Redux based state management library over here, and you are good to go.
You start by adding the following packages to your Blazor Server/WASM application,
Register Fluxor services with the default IoC container. You would do that in the Program.cs file,
Inside index.html, add the script reference for Fluxor.Blazor.Web,
Store:
As the name suggests, a store persists in an application state tree, i.e., the build-out of different feature states. The Store has to be initialized when the application first kicks off. Hence, it is best to put out the Store initialization logic in the App component,
Feature:
Feature is an area that provides a new piece of state for the Store. To declare a feature, you would want to extend the abstract Feature<T> class available in Fluxor. Feature<T> takes a state type. The following feature class can be used to name a feature and an initial state for the CounterState type,
The state contains nothing but some properties. A state should be immutable in nature. You should never mutate a state directly, rather return a new state by changing its different properties. Hence, we can go for a record instead of a class,
CounterState contains a init only property Count and it is initialized with the value 0.
Action:
A state should change based on different actions dispatched on it. An action may or may not have arguments. For example, an IncrementCounterAction does nothing but increment the Count property by 1; but if you want to change the increment value to something more dynamic, use arguments.
Reducer:
A reducer is a pure function that takes the current state and action being dispatched upon it. Depending on the action type, it produces a new state and returns it.
Effect:
Effects are used to handle side effects in your application. A side effect can be anything from a long-running task to an HTTP call to service. In these cases, state changes are not instantaneous. Effects perform tasks, which are synchronous/asynchronous, and dispatch different actions based on the outcome. The following uses the HttpClient to asynchronously get the content of the sample-data/weather.json file.
Check the FetchDataUseCase to get a better understanding of the related state, actions, reducers and effects.
Accessing State:
Feature states can be injected into a razor component using the IState<T> interface. The following shows how to inject the Counter state into the Counter.razor component,
You use the Value property to get access to different properties of the injected state
Dispatching Action:
To dispatch an action on the store depending on a UI event, inject the IDispatcher service and use the Dispatch the method passing the type of action.
A similar approach is taken FetchData component. Although one thing to notice here is the inheritance of the component from FluxorComponent. Dispatching of the FetchDataSuccessAction and FetchDataErrorAction is happening from within the Effects. The UI has to be notified i.e. call StateHasChanged when these actions are dispatched. Using the FluxorComponent takes care of that.
Redux DevTools
To tinker around with the application state, you should have the Redux DevTools extension installed in your preferred browser. Open up the Developer Console of your browser and go to the Redux tab. You will see what actions are dispatched and how your application state is modified.