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 in your Blazor Server/WASM application,
Register Fluxor services with the default IoC container. You would do that in Program.cs file,
Inside index.html, add the script reference for Fluxor.Blazor.Web,
As the name suggests, a store persists an application state tree, i.e., 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 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.
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.
A reducer is a pure function that takes the current state and an action been dispatched upon it. Depending on the action type, it produces a new state and returns it.
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 a 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.
Feature states can be injected in a razor component using the IState<T> interface. Following shows how to inject the Counter state in the Counter.razor component,
You use the Value property to get access to different properties of the injected state
To dispatch an action on the store depending on a UI event, inject the IDispatcher service and use the Dispatch method passing the type of the 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.
To tinker around with application state, you should have the Redux DevTools extension installed in your preferred browser. Open up the Developer Console, go to the Redux tab. You will see what actions are dispatched and how your application state is modified.