### Example Application Output Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md This is an example of the expected output when running the Fluxor application, showing store initialization and state changes after actions. ```text Initializing store 1: Increment counter x: Exit > 1 ==========================> CounterState ClickCount is 1 <========================== CounterState 1: Increment counter x: Exit > 1 ==========================> CounterState ClickCount is 2 <========================== CounterState 1: Increment counter x: Exit > ``` -------------------------------- ### Fluxor App Initialization and Event Loop Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01E-ActionSubscriber/README.md Sets up the Fluxor store, dispatches actions, and runs an event loop to interact with the user. Includes basic setup for the store and action dispatcher. ```csharp public class App : IDisposable { private readonly IStore Store; public readonly IDispatcher Dispatcher; private readonly IActionSubscriber ActionSubscriber; public App(IStore store, IDispatcher dispatcher, IActionSubscriber actionSubscriber) { Store = store; Dispatcher = dispatcher; ActionSubscriber = actionSubscriber; } public void Run() { Console.Clear(); Console.WriteLine("Initializing store"); Store.InitializeAsync().Wait(); SubscribeToResultAction(); string input = ""; do { Console.WriteLine("1: Get mutable object from API server"); Console.WriteLine("x: Exit"); Console.Write("> "); input = Console.ReadLine(); switch (input.ToLowerInvariant()) { case "1": var getCustomerAction = new GetCustomerForEditAction(42); Dispatcher.Dispatch(getCustomerAction); break; case "x": Console.WriteLine("Program terminated"); return; } } while (true); } private void SubscribeToResultAction() { // We'll implement this shortly } void IDisposable.Dispose() { // We'll implement this shortly } } ``` -------------------------------- ### Create Logging Middleware in C# Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01C-MiddlewareTutorial/README.md Implement a custom middleware by descending from Fluxor.Middleware. This example shows how to store a reference to the IStore and log when the middleware is initialized. ```csharp public class LoggingMiddleware : Middleware { private IStore Store; public override Task InitializeAsync(IDispatcher dispatcher, IStore store) { Store = store; Console.WriteLine(nameof(InitializeAsync)); return Task.CompletedTask; } } ``` -------------------------------- ### AfterDispatch Method Implementation Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02C-MiddlewareTutorial/README.md Implement the AfterDispatch method to execute logic after an action has been dispatched. This example serializes and logs the state of all features within the store. ```csharp public override void AfterDispatch(object action) { Console.WriteLine(nameof(AfterDispatch) + ObjectInfo(action)); Console.WriteLine(" ===========STATE AFTER DISPATCH==========="); foreach (KeyValuePair feature in Store.Features) { string json = JsonSerializer.Serialize(feature.Value, JsonOptions) .Replace("\n", "\n\t"); Console.WriteLine(" " + feature.Key + ": " + json); } Console.WriteLine(); } ``` -------------------------------- ### Skeleton App Class Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md A basic 'App' class structure required for dependency injection setup. This class will later be expanded to interact with the Fluxor store. ```csharp public class App { public void Run() { } } ``` -------------------------------- ### Dispatching FetchDataAction in App class Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Dispatch the FetchDataAction when option '2' is chosen in the console menu. This example shows how to dispatch actions within a switch statement. ```csharp public void Run() { ... Console.WriteLine("2: Fetch data"); ... switch(input.ToLowerInvariant()) { case "1": var incrementCounterActionction = new IncrementCounterAction(); Dispatcher.Dispatch(incrementCounterActionction); break; case "2": var fetchDataAction = new FetchDataAction(); Dispatcher.Dispatch(fetchDataAction); break; case "x": Console.WriteLine("Program terminated"); return; } ... ``` -------------------------------- ### Console Output of Action Notification Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01E-ActionSubscriber/README.md This output shows an example of what the console might display when an action is dispatched and an action subscriber is active. It includes the action type and its associated payload. ```text > Action notification: GetCustomerForEditResultAction { "Id": 42, "RowVersion": "AQIDBAUGBwgJCgsMDQ4PEA==", "Name": "Our first customer" } ``` -------------------------------- ### EffectMethod on Generic Classes in Fluxor Source: https://github.com/mrpmorris/fluxor/blob/master/Docs/releases.md Fluxor enables the declaration of [EffectMethod] on generic classes, facilitating the creation of generic effect handlers. The example shows 'GenericEffectClassForMyAction' and 'GenericEffectClassForAnotherAction' inheriting from a generic abstract class. ```csharp public class GenericEffectClassForMyAction : AbstractGenericEffectClass { public GenericEffectClassForMyAction(SomeService someService) : base(someService) { } } public class GenericEffectClassForAnotherAction : AbstractGenericEffectClass { public GenericEffectClassForAnotherAction(SomeService someService) : base(someService) { } } abstract class AbstractGenericEffectClass { private readonly ISomeService SomeService; protected AbstractGenericEffectClass(ISomeService someService) { SomeService = someService; } [EffectMethod] public Task HandleTheActionAsync(T action, IDispatcher dispatcher) { return SomeService.DoSomethingAsync(action); } } ``` -------------------------------- ### App Class with State Management Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md The App class demonstrates initializing the store, handling user input, and dispatching actions. It also subscribes to state changes to log updates. ```csharp public class App { private readonly IStore Store; public readonly IDispatcher Dispatcher; public readonly IState CounterState; public App(IStore store, IDispatcher dispatcher, IState counterState) { Store = store; Dispatcher = dispatcher; CounterState = counterState; CounterState.StateChanged += CounterState_StateChanged; } private void CounterState_StateChanged(object sender, EventArgs e) { Console.WriteLine(""); Console.WriteLine("==========================> CounterState"); Console.WriteLine("ClickCount is " + CounterState.Value.ClickCount); Console.WriteLine("<========================== CounterState"); Console.WriteLine(""); } public void Run() { Console.Clear(); Console.WriteLine("Initializing store"); Store.InitializeAsync().Wait(); string input = ""; do { Console.WriteLine("1: Increment counter"); Console.WriteLine("x: Exit"); Console.Write("> "); input = Console.ReadLine(); switch(input.ToLowerInvariant()) { case "1": var action = new IncrementCounterAction(); Dispatcher.Dispatch(action); break; case "x": Console.WriteLine("Program terminated"); return; } } while (true); } } ``` -------------------------------- ### Initialize Fluxor Store in App.cs Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md Injects the IStore and initializes it asynchronously. This step is crucial for Fluxor to begin managing state. ```csharp using Fluxor; public class App { private readonly IStore Store; public App(IStore store) { Store = store; } public void Run() { Console.Clear(); Console.WriteLine("Initializing store"); Store.InitializeAsync().Wait(); } } ``` -------------------------------- ### Create Mock WeatherForecast Service Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Implements an interface to simulate fetching weather forecast data asynchronously. Includes a delay to mimic network latency. ```csharp using YourAppName.Shared; public interface IWeatherForecastService { Task GetForecastAsync(DateTime startDate); } public class WeatherForecastService : IWeatherForecastService { private static readonly string[] Summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; public async Task GetForecastAsync(DateTime startDate) { await Task.Delay(1000); // Simulate a 1 second response time var rng = new Random(); return Enumerable.Range(1, 2).Select(index => new WeatherForecast { Date = startDate.AddDays(index), TemperatureC = rng.Next(-20, 55), Summary = Summaries[rng.Next(Summaries.Length)] }) .ToArray(); } } ``` -------------------------------- ### Fluxor Bootstrapping in Program.cs Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01E-ActionSubscriber/README.md Configures the service provider with Fluxor services and registers the main application class. This code should replace the existing Main method. ```csharp static void Main(string[] args) { var services = new ServiceCollection(); services.AddScoped(); services.AddFluxor(o => o .ScanAssemblies(typeof(Program).Assembly)); IServiceProvider serviceProvider = services.BuildServiceProvider(); var app = serviceProvider.GetRequiredService(); app.Run(); } ``` -------------------------------- ### Configure Fluxor in Program.cs Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md Sets up dependency injection and initializes Fluxor by scanning the application's assemblies for Fluxor-related components. Ensure Fluxor NuGet package is added. ```csharp class Program { static void Main(string[] args) { var services = new ServiceCollection(); services.AddScoped(); services.AddFluxor(o => o .ScanAssemblies(typeof(Program).Assembly)); IServiceProvider serviceProvider = services.BuildServiceProvider(); var app = serviceProvider.GetRequiredService(); app.Run(); } } ``` -------------------------------- ### InitializeAsync Method Implementation Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02C-MiddlewareTutorial/README.md Implement the InitializeAsync method to set up middleware, store references, and perform initializations. This method is called when the store is initialized. ```csharp private IStore Store = null!; private static readonly JsonSerializerOptions JsonOptions = new JsonSerializerOptions { WriteIndented = true }; public override Task InitializeAsync(IDispatcher dispatcher, IStore store) { Store = store; Console.WriteLine(nameof(InitializeAsync)); return Task.CompletedTask; } ``` -------------------------------- ### Create Logging Middleware Base Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02C-MiddlewareTutorial/README.md Implement the base LoggingMiddleware class, inheriting from Fluxor.Middleware. This initial version intercepts the store initialization. ```csharp public class LoggingMiddleware : Middleware { public override Task InitializeAsync(IDispatcher dispatcher, IStore store) { Console.WriteLine(nameof(InitializeAsync)); return Task.CompletedTask; } } ``` -------------------------------- ### Simulate Server Data Fetching with Effect Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md This effect simulates fetching weather forecasts from a server. It waits for a second, generates random forecast data, and then dispatches a result action. Use this to handle asynchronous operations triggered by an action. ```csharp public class Effects { private readonly static string[] Summaries = ["Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"]; [EffectMethod(typeof(FetchForecastsAction))] public async Task HandleFetchForecastsAction(IDispatcher dispatcher) { // Simulate a delay await Task.Delay(1_000); DateOnly startDate = DateOnly.FromDateTime(DateTime.Now); var forecasts = Enumerable.Range(1, 5) .Select( index => new WeatherForecast( Date: startDate.AddDays(index), TemperatureC: Random.Shared.Next(-20, 55), Summary: Summaries[Random.Shared.Next(Summaries.Length)] ) ); var action = new FetchForecastsResultAction(forecasts); dispatcher.Dispatch(action); } } ``` -------------------------------- ### Inject and Subscribe to WeatherState Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Injects `IState` into the `App` class and subscribes to its `StateChanged` event. This allows the application to react to changes in the weather state. ```csharp public class App { ... private readonly IState WeatherState; public App( ... IState weatherState) { ... WeatherState = weatherState; WeatherState.StateChanged += WeatherState_StateChanged; } } ``` -------------------------------- ### Enhanced LoggingMiddleware with State Inspection Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01C-MiddlewareTutorial/README.md An extended `LoggingMiddleware` that, in addition to logging actions, iterates through all features in the store and outputs their current state in JSON format after each action dispatch. This provides a comprehensive view of the store's state changes. ```csharp public override void AfterDispatch(object action) { Console.WriteLine(nameof(AfterDispatch) + ObjectInfo(action)); Console.WriteLine("\t===========STATE AFTER DISPATCH==========="); foreach (KeyValuePair feature in Store.Features) { string json = JsonConvert.SerializeObject(feature.Value, Formatting.Indented) .Replace("\n", "\n\t"); Console.WriteLine("\t" + feature.Key + ": " + json); } Console.WriteLine(); } ``` -------------------------------- ### Register Mock Service in Program.cs Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Registers the mock `WeatherForecastService` with the dependency injection container. This makes it available for injection into other services or components. ```csharp services.AddScoped(); ``` -------------------------------- ### Implement Full Logging Middleware Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02C-MiddlewareTutorial/README.md Extend the LoggingMiddleware to override all relevant lifecycle methods for comprehensive logging of actions and state changes. Includes a helper method for serializing object information. ```csharp public class LoggingMiddleware : Middleware { public override Task InitializeAsync(IDispatcher dispatcher, IStore store) { Console.WriteLine(nameof(InitializeAsync)); return Task.CompletedTask; } public override void AfterInitializeAllMiddlewares() { Console.WriteLine(nameof(AfterInitializeAllMiddlewares)); } public override bool MayDispatchAction(object action) { Console.WriteLine(nameof(MayDispatchAction) + ObjectInfo(action)); return true; } public override void BeforeDispatch(object action) { Console.WriteLine(nameof(BeforeDispatch) + ObjectInfo(action)); } public override void AfterDispatch(object action) { Console.WriteLine(nameof(AfterDispatch) + ObjectInfo(action)); Console.WriteLine(); } private string ObjectInfo(object obj) => ": " + obj.GetType().Name + " " + JsonSerializer.Serialize(obj); } ``` -------------------------------- ### Action to Fetch Customer Data Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01E-ActionSubscriber/README.md Defines an action to initiate fetching customer data by ID. This action is dispatched to trigger the data retrieval process. ```csharp public class GetCustomerForEditAction { public int Id { get; } public GetCustomerForEditAction(int id) { Id = id; } } ``` -------------------------------- ### LoggingMiddleware Implementation Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01C-MiddlewareTutorial/README.md A custom middleware that logs initialization, action dispatching, and object information to the console. It overrides several `Middleware` methods to hook into the Fluxor lifecycle. ```csharp public class LoggingMiddleware : Middleware { private IStore Store; public override Task InitializeAsync(IDispatcher dispatcher, IStore store) { Store = store; Console.WriteLine(nameof(InitializeAsync)); return Task.CompletedTask; } public override void AfterInitializeAllMiddlewares() { Console.WriteLine(nameof(AfterInitializeAllMiddlewares)); } public override bool MayDispatchAction(object action) { Console.WriteLine(nameof(MayDispatchAction) + ObjectInfo(action)); return true; } public override void BeforeDispatch(object action) { Console.WriteLine(nameof(BeforeDispatch) + ObjectInfo(action)); } public override void AfterDispatch(object action) { Console.WriteLine(nameof(AfterDispatch) + ObjectInfo(action)); Console.WriteLine(); } private string ObjectInfo(object obj) => ": " + obj.GetType().Name + " " + JsonConvert.SerializeObject(obj, Formatting.Indented); } ``` -------------------------------- ### Define WeatherState Class Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Defines the state for the weather use-case, including a boolean to track loading status and an enumerable for forecast data. Includes a private constructor for initial state creation. ```csharp using YourAppName.Store.Shared; [FeatureState] public class WeatherState { public bool IsLoading { get; } public IEnumerable Forecasts { get; } private WeatherState() {} // Required for creating initial state public WeatherState(bool isLoading, IEnumerable forecasts) { IsLoading = isLoading; Forecasts = forecasts ?? Array.Empty(); } } ``` -------------------------------- ### Effect handling FetchDataAction by descending from Effect Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Handle FetchDataAction by inheriting from Effect. This approach requires overriding the HandleAsync method and allows for dependency injection. ```csharp public class FetchDataActionEffect : Effect { private readonly IWeatherForecastService WeatherForecastService; public FetchDataActionEffect(IWeatherForecastService weatherForecastService) { WeatherForecastService = weatherForecastService; } public override async Task HandleAsync(FetchDataAction action, IDispatcher dispatcher) { var forecasts = await WeatherForecastService.GetForecastAsync(DateTime.Now); dispatcher.Dispatch(new FetchDataResultAction(forecasts)); } ``` -------------------------------- ### Initialize Fluxor Store in Blazor Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02A-StateActionsReducersTutorial/README.md Ensure the Fluxor store is initialized by adding the StoreInitializer component. Place this markup above the `` component in `App.razor` for standalone apps, or `Routes.razor` for other app types. ```html ``` -------------------------------- ### Middleware State Logging Output Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02C-MiddlewareTutorial/README.md Observe the console output after implementing the AfterDispatch method to log feature states. This shows the state of 'Weather' and 'Counter' features before and after an action. ```text InitializeAsync AfterInitializeAllMiddlewares MayDispatchAction: StoreInitializedAction {} BeforeDispatch: StoreInitializedAction {} AfterDispatch: StoreInitializedAction {} ===========STATE AFTER DISPATCH=========== Weather: { "State": { "IsLoading": false, "Forecasts": [] } } Counter: { "State": { "ClickCount": 0 } } *** Increment counter MayDispatchAction: IncrementCounterAction {} BeforeDispatch: IncrementCounterAction {} AfterDispatch: IncrementCounterAction {} ===========STATE AFTER DISPATCH=========== Weather: { "State": { "IsLoading": false, "Forecasts": [] } } Counter: { "State": { "ClickCount": 1 } } ``` -------------------------------- ### Reducer to handle FetchDataAction Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Use this reducer to set the IsLoading state to true when FetchDataAction is dispatched. It does not use any values from the action. ```csharp public static class Reducers { [ReducerMethod] public static WeatherState ReduceFetchDataAction(WeatherState state, FetchDataAction action) => new WeatherState( isLoading: true, forecasts: null); } ``` ```csharp public static class Reducers { [ReducerMethod(typeof(FetchDataAction))] public static WeatherState ReduceFetchDataAction(WeatherState state) => new WeatherState( isLoading: true, forecasts: null); } ``` -------------------------------- ### Split Reducer Methods Across Multiple Classes Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md Organize reducer logic by placing `[ReducerMethod]` decorated methods in different static classes. The `Reducer` class name is not special. ```csharp public static class SomeReducerClass { [ReducerMethod] public static SomeState ReduceSomeAction(SomeState state, SomeAction action) => new SomeState(); [ReducerMethod] public static SomeState ReduceSomeAction2(SomeState state, SomeAction2 action) => new SomeState(); } public static class SomeOtherReducerClass { [ReducerMethod] public static SomeState ReduceSomeAction3(SomeState state, SomeAction3 action) => new SomeState(); [ReducerMethod] public static SomeState ReduceSomeAction4(SomeState state, SomeAction4 action) => new SomeState(); } ``` -------------------------------- ### Define FetchDataResultAction Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Create a new class to hold the results of a server call, which will be used to update the application state. ```csharp public class FetchDataResultAction { public IEnumerable Forecasts { get; } public FetchDataResultAction(IEnumerable forecasts) { Forecasts = forecasts; } } ``` -------------------------------- ### Configure Redux Dev Tools Options Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02D-ReduxDevToolsTutorial/README.md Customize the Redux Dev Tools integration by passing options to the `UseReduxTools` method. Options include setting the display name, action latency, and maximum history length. ```csharp services.AddFluxor(o => o.ScanAssemblies(typeof(SomeType).Assembly), o.UseReduxDevTools(rdt => { rdt.Name = "My application"; })); ``` -------------------------------- ### Reducer Class Implementation Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md An alternative pattern to define a reducer per state and action combination using a `Reducer` class. This pattern is generally not recommended due to increased code verbosity. ```csharp public class IncrementCounterReducer : Reducer { public override CounterState Reduce(CounterState state, IncrementCounterAction action) => new CounterState(clickCount: state.ClickCount + 1); } ``` -------------------------------- ### Dispatch FetchForecastsAction Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md Inject IDispatcher and dispatch a FetchForecastsAction in the OnInitialized lifecycle method to initiate data fetching. ```razor @page "/weather" @inherits Fluxor.Blazor.Web.Components.FluxorComponent @using FluxorBlazorWeb.EffectsTutorial.Store.WeatherFeature @inject IDispatcher Dispatcher @inject IState WeatherState ... @code { protected override void OnInitialized() { base.OnInitialized(); var action = new FetchForecastsAction(); Dispatcher.Dispatch(action); } } ``` -------------------------------- ### Subscribe to State Changes in App.cs Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md Injects IState and subscribes to its StateChanged event. This allows the application to react whenever the CounterState is updated. ```csharp using YourAppName.Store.CounterUseCase; public class App { private readonly IStore Store; private readonly IState CounterState; public App(IStore store, IState counterState) { Store = store; CounterState = counterState; CounterState.StateChanged += CounterState_StateChanged; } ... ``` -------------------------------- ### Subscribing to Action Results Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01E-ActionSubscriber/README.md Implements the `SubscribeToResultAction` method to subscribe to `GetCustomerForEditResultAction`. This allows reacting to the action by logging the received customer data. ```csharp private void SubscribeToResultAction() { Console.WriteLine($"Subscribing to action {nameof(GetCustomerForEditResultAction)}"); ActionSubscriber.SubscribeToAction(this, action => { // Show the object from the server in the console string jsonToShowInConsole = JsonConvert.SerializeObject(action.Customer, Formatting.Indented); Console.WriteLine("Action notification: " + action.GetType().Name); Console.WriteLine(jsonToShowInConsole); }); } ``` -------------------------------- ### Action to Receive Customer Data Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01E-ActionSubscriber/README.md Defines an action to carry the customer data after it has been fetched. This action is dispatched by the effect that retrieves the data. ```csharp public class GetCustomerForEditResultAction { public CustomerEdit Customer { get; } public GetCustomerForEditResultAction(CustomerEdit customer) { Customer = customer; } } ``` -------------------------------- ### Reducers for Customer Edit Actions Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01E-ActionSubscriber/README.md Implements reducers to handle `GetCustomerForEditAction` and `GetCustomerForEditResultAction`. These reducers update the `EditCustomerState` based on the dispatched actions. ```csharp public static class Reducers { [ReducerMethod(typeof(GetCustomerForEditAction))] public static EditCustomerState Reduce(EditCustomerState state) => new EditCustomerState(isLoading: true); [ReducerMethod(typeof(GetCustomerForEditResultAction))] public static EditCustomerState Reduce(EditCustomerState state) => new EditCustomerState(isLoading: false); } ``` -------------------------------- ### Display Forecasts Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md Iterate over the Forecasts collection from the WeatherState to display weather data. ```razor @foreach (var forecast in WeatherState.Value.Forecasts) ``` -------------------------------- ### Effect to Handle Fetching Customer Data Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01E-ActionSubscriber/README.md An effect that listens for `GetCustomerForEditAction`, simulates fetching data from a server, and dispatches `GetCustomerForEditResultAction` with the retrieved data. ```csharp public class Effects { [EffectMethod(typeof(GetCustomerForEditAction))] public async Task HandleGetCustomerForEditAction(IDispatcher dispatcher) { Console.WriteLine("Getting customer with Id: 42"); await Task.Delay(1000); string jsonFromServer = $"{{"Id":42,"RowVersion":"AQIDBAUGBwgJCgsMDQ4PEA==","Name":"Our first customer"}}"; var objectFromServer = JsonConvert.DeserializeObject(jsonFromServer); dispatcher.Dispatch(new GetCustomerForEditResultAction(objectFromServer)); } } ``` -------------------------------- ### Object Serialization Helper Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01C-MiddlewareTutorial/README.md A helper method to serialize any object into an indented JSON string for logging purposes. Requires the `Newtonsoft.Json` library. ```csharp private string ObjectInfo(object obj) => ": " + obj.GetType().Name + " " + JsonConvert.SerializeObject(obj, Formatting.Indented); ``` -------------------------------- ### Define WeatherState Record Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md Defines the state for the weather feature, including loading status and forecast data. ```csharp [FeatureState] public record WeatherState( bool IsLoading, ImmutableList Forecasts) { public WeatherState() : this( IsLoading: false, Forecasts: []) { } } ``` -------------------------------- ### Log WeatherState Changes to Console Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Implements the `WeatherState_StateChanged` event handler to log the current `WeatherState` to the console. It displays loading status and weather forecast details. ```csharp private void WeatherState_StateChanged(object sender, EventArgs e) { Console.WriteLine(""); Console.WriteLine("=========================> WeatherState"); Console.WriteLine("IsLoading: " + WeatherState.Value.IsLoading); if (!WeatherState.Value.Forecasts.Any()) { Console.WriteLine("--- No weather forecasts"); } else { Console.WriteLine("Temp C\tTemp F\tSummary"); foreach (WeatherForecast forecast in WeatherState.Value.Forecasts) Console.WriteLine($"{forecast.TemperatureC}\t{forecast.TemperatureF}\t{forecast.Summary}"); } Console.WriteLine("<========================== WeatherState"); Console.WriteLine(""); } ``` -------------------------------- ### Define Immutable Counter State (Class) Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02A-StateActionsReducersTutorial/README.md Alternatively, define the state using a non-record class. Ensure it is immutable and includes a parameterless constructor for initial state. ```csharp [FeatureState] public class CounterState { public int ClickCount { get; } // Required for creating initial state private CounterState() {} public CounterState(int clickCount) { ClickCount = clickCount; } } ``` -------------------------------- ### Define WeatherForecast Record Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md Defines the data structure for a weather forecast, including temperature conversion. ```csharp public record WeatherForecast(DateOnly Date, int TemperatureC, string Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); } ``` -------------------------------- ### Fluxor State Flow Diagram Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02A-StateActionsReducersTutorial/README.md Illustrates the typical flow of state changes in a Fluxor application, from UI dispatching actions to reducers updating state and notifying the UI. ```mermaid sequenceDiagram participant UI participant Fluxor participant Reducers UI->>Fluxor: Dispatch IncrementCountAction Fluxor->>Reducers: Call Reducer with State & IncrementCountAction Reducers-->>Fluxor: State update (ClickCount = ClickCount + 1) Fluxor-->>UI: State has changed ``` -------------------------------- ### Effect handling FetchDataAction with action parameter Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Handle FetchDataAction by fetching data and dispatching a FetchDataResultAction. This effect method includes the action parameter, allowing access to its values. ```csharp public class Effects { private readonly IWeatherForecastService WeatherForecastService; public Effects(IWeatherForecastService weatherForecastService) { WeatherForecastService = weatherForecastService; } [EffectMethod] public async Task HandleFetchDataAction(FetchDataAction action, IDispatcher dispatcher) { var forecasts = await WeatherForecastService.GetForecastAsync(DateTime.Now); dispatcher.Dispatch(new FetchDataResultAction(forecasts)); } ``` -------------------------------- ### Reduce Action Without Parameter in Signature Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md Use `[ReducerMethod]` with the action type to avoid unused parameter warnings when the action's payload is not needed. ```csharp [ReducerMethod(typeof(IncrementCounterAction))] public static CounterState ReduceIncrementCounterAction(CounterState state) => new CounterState(clickCount: state.ClickCount + 1); ``` -------------------------------- ### Feature State for Editing Customer Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01E-ActionSubscriber/README.md Defines the state for the customer editing feature, indicating if data is currently being loaded. A private constructor is required for initial state creation. ```csharp [FeatureState] public class EditCustomerState { public bool IsLoading { get; private set; } private EditCustomerState() { } // Required for creating initial state public EditCustomerState(bool isLoading) { IsLoading = isLoading; } } ``` -------------------------------- ### Register Fluxor Services Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02A-StateActionsReducersTutorial/README.md Add Fluxor to your Blazor application's service collection. Replace `{SomeType}` with a type from your application's assembly or the assembly containing your Fluxor states and actions. ```csharp builder.Services.AddFluxor(x => x.ScanAssemblies(typeof({SomeType}).Assembly)); ``` -------------------------------- ### Register Middleware with Fluxor in C# Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01C-MiddlewareTutorial/README.md Add your custom middleware to the Fluxor store configuration in the Program class. This is done by calling AddMiddleware after ScanAssemblies. ```csharp services.AddFluxor(o => o .ScanAssemblies(typeof(Program).Assembly) .AddMiddleware()); ``` -------------------------------- ### Reducer to Set Loading State Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md Defines a reducer method that sets the IsLoading state to true when a FetchForecastsAction is dispatched. ```csharp public static class Reducers { [ReducerMethod] public static WeatherState ReduceFetchForecastsAction(WeatherState state, FetchForecastsAction action) => new WeatherState(IsLoading: true, Forecasts: []); } ``` -------------------------------- ### Display Loading State Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md Conditionally render content based on the IsLoading property of the WeatherState. ```razor @if (WeatherState.Value.IsLoading) ``` -------------------------------- ### Effect Method without Action Parameter Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md This demonstrates defining an Effect handler using the `[EffectMethod]` attribute without explicitly including the action parameter in the method signature. The action type is specified in the attribute. ```csharp [EffectMethod(typeof(FetchForecastsAction))] public async Task HandleFetchForecastsAction(IDispatcher dispatcher) { // code here } } ``` -------------------------------- ### Disposing IDisposable from BeginMiddlewareChange Source: https://github.com/mrpmorris/fluxor/blob/master/Docs/disposable-callback-not-disposed.md In advanced scenarios involving middleware, always dispose of the IDisposable returned by IStore.BeginInternalMiddlewareChange to avoid resource leaks. ```csharp using (Store.BeginMiddlewareChange()) { //etc } ``` -------------------------------- ### ReducerMethod on Generic Classes in Fluxor Source: https://github.com/mrpmorris/fluxor/blob/master/Docs/releases.md Fluxor now supports declaring [ReducerMethod] on generic classes. This allows for reusable reducer logic across different state types, as demonstrated with 'TestIntReducer' and 'TestStringReducer'. ```csharp public class TestIntReducer: AbstractGenericStateReducers { } public class TestStringReducer: AbstractGenericStateReducers { } abstract class AbstractGenericStateReducers where T : IEquatable { [ReducerMethod] public static TestState ReduceRemoveItemAction(TestState state, RemoveItemAction action) { new TestState(state.Items.Where(x => !x.Equals(action.Item)).ToArray()); } } ``` -------------------------------- ### Middleware Action Interception Output Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02C-MiddlewareTutorial/README.md Observe the console output when actions are dispatched through middleware. This shows the sequence of events: MayDispatchAction, BeforeDispatch, and AfterDispatch. ```text InitializeAsync AfterInitializeAllMiddlewares MayDispatchAction: StoreInitializedAction {} BeforeDispatch: StoreInitializedAction {} AfterDispatch: StoreInitializedAction {} ``` ```text MayDispatchAction: IncrementCounterAction {} BeforeDispatch: IncrementCounterAction {} AfterDispatch: IncrementCounterAction {} ``` ```text *** The FetchForecastsAction is dispatched. MayDispatchAction: FetchForecastsAction {} BeforeDispatch: FetchForecastsAction {} AfterDispatch: FetchForecastsAction {} *** As there is an effect to fetch data that is triggered by FetchForecastsAction, the store triggers it here but does not wait for it to complete. *** The dispatch of FetchForecastsAction is now complete. *** Later, the effect receives data from the mock server and dispatches that data in a new FetchForecastsResultAction. MayDispatchAction: FetchForecastsResultAction { "Forecasts": [...data...] } BeforeDispatch: FetchForecastsResultAction { "Forecasts": [...data...] } AfterDispatch: FetchForecastsResultAction { "Forecasts": [...data...] } ``` -------------------------------- ### Define Immutable Counter State (Record) Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02A-StateActionsReducersTutorial/README.md Define the state for the counter feature using an immutable record. A parameterless constructor is required for initial state creation. ```csharp [FeatureState] public record CounterState(int ClickCount) { // Required for creating initial state public CounterState() : this(0) { } } ``` -------------------------------- ### Reducer without Action Parameter Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md An alternative reducer definition that omits the action parameter when its values are not needed. ```csharp public static class Reducers { [ReducerMethod(typeof(FetchForecastsAction))] public static WeatherState ReduceFetchForecastsAction(WeatherState state) => new WeatherState(IsLoading: true, Forecasts: []); } ``` -------------------------------- ### Fluxor Effect Lifecycle Diagram Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md Visualizes the flow of actions and state changes through UI, Fluxor, Reducers, Effects, and Server components. ```mermaid sequenceDiagram participant UI participant Fluxor participant Reducers participant Effects participant Server UI->>Fluxor: Dispatch FetchForecastsAction Fluxor->>Reducers: Call Reducer with FetchForecastsAction Reducers-->>Fluxor: State update (IsLoading=True, Forecasts=[]) Fluxor-->>UI: State has changed Fluxor->>Effects: Trigger Effect with FetchForecastsAction Effects->>Server: Call server Server-->>Effects: Return data Effects->>Fluxor: Dispatch FetchForecastsResultAction(Forecasts=data) Fluxor->>Reducers: Call Reducer with FetchForecastsResultAction Reducers-->>Fluxor: State update (IsLoading=False, Forecasts=action.Forecasts) Fluxor-->>UI: State has changed ``` -------------------------------- ### Handle State Changes Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md A method that is called when the CounterState changes. It logs the current value of ClickCount to the console. ```csharp private void CounterState_StateChanged(object sender, EventArgs e) { Console.WriteLine(""); Console.WriteLine("==========================> CounterState"); Console.WriteLine("ClickCount is " + CounterState.Value.ClickCount); Console.WriteLine("<========================== CounterState"); Console.WriteLine(""); } ``` -------------------------------- ### Enable Redux Dev Tools in Fluxor Options Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02D-ReduxDevToolsTutorial/README.md Use the `UseReduxTools` extension method on the Fluxor options builder to enable Redux Dev Tools integration. This should be placed within the conditional DEBUG block. ```csharp builder.Services.AddFluxor(o => { o.ScanAssemblies(typeof(SomeType).Assembly); o.UseRouting(); // Add this to support routing via Fluxor actions #if DEBUG o.UseReduxDevTools(); #endif }); ``` -------------------------------- ### Reduce FetchDataResultAction in Reducers.cs Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Add a new ReducerMethod to reduce the contents of the FetchDataResultAction into the application state. This sets IsLoading to false and updates the forecasts. ```csharp [ReducerMethod] public static WeatherState ReduceFetchDataResultAction(WeatherState state, FetchDataResultAction action) => new WeatherState( isLoading: false, forecasts: action.Forecasts); ``` -------------------------------- ### Calling base.Dispose in FluxorComponent/FluxorLayout Source: https://github.com/mrpmorris/fluxor/blob/master/Docs/disposable-callback-not-disposed.md When overriding the Dispose method in FluxorComponent or FluxorLayout, ensure you call the base Dispose method to properly clean up resources and prevent memory leaks. ```csharp protected override void Dispose(bool disposed) { base.Dispose(disposed); // etc } ``` -------------------------------- ### Dispatching an IncrementCounterAction Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md Dispatch an action to signal an intention to change the state. Ensure the IDispatcher is injected into your class. ```csharp var action = new IncrementCounterAction(); Dispatcher.Dispatch(action); ``` -------------------------------- ### Enable Stack Trace Logging in Redux Dev Tools Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02D-ReduxDevToolsTutorial/README.md Enable Fluxor to pass stack traces to Redux Dev Tools for easier debugging of action dispatch origins. This can be configured with a limit and a filter expression. ```csharp o.UseReduxDevTools(rdt => { rdt.Name = "My application"; rdt.EnableStackTrace(); })); ``` -------------------------------- ### Reducer Method for Incrementing Counter Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md Define a static reducer method to handle the IncrementCounterAction. This method takes the current state and the action, returning a new state object with the updated click count. ```csharp public static class Reducers { [ReducerMethod] public static CounterState ReduceIncrementCounterAction(CounterState state, IncrementCounterAction action) => new CounterState(clickCount: state.ClickCount + 1); } ``` -------------------------------- ### Effect handling FetchDataAction without action parameter Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Handle FetchDataAction by dispatching a FetchDataResultAction after fetching data. This effect method does not require the action parameter. ```csharp [EffectMethod(typeof(FetchDataAction))] public async Task HandleFetchDataAction(IDispatcher dispatcher) { var forecasts = await WeatherForecastService.GetForecastAsync(DateTime.Now); dispatcher.Dispatch(new FetchDataResultAction(forecasts)); } ``` -------------------------------- ### Effect Method with Action Parameter Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md An alternative way to define an Effect handler by including the action type directly in the method signature. This is useful when you need to access the dispatched action's properties within the handler. ```csharp [EffectMethod] public async Task HandleFetchForecastsAction(FetchForecastsAction action, IDispatcher dispatcher) { // code here } } ``` -------------------------------- ### Define Fluxor Feature State Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01A-StateActionsReducersTutorial/README.md Defines an immutable state class decorated with [FeatureState]. It requires a parameterless constructor for initial state creation and a constructor for setting initial values. ```csharp [FeatureState] public class CounterState { public int ClickCount { get; } private CounterState() {} // Required for creating initial state public CounterState(int clickCount) { ClickCount = clickCount; } } ``` -------------------------------- ### Add Redux Dev Tools Package Conditionally Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02D-ReduxDevToolsTutorial/README.md Add the Fluxor.Blazor.Web.ReduxDevTools NuGet package to your project. This should be conditional on DEBUG mode to avoid including it in release builds. ```xml ``` -------------------------------- ### Inject WeatherState Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md Inject the IState interface to access the current weather state within a Blazor component. ```razor @using {Namespace for your WeatherFeature} @inject IState WeatherState ``` -------------------------------- ### Reduce Effect Result into State Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02B-EffectsTutorial/README.md This reducer method updates the application state with the fetched weather forecasts. It sets `IsLoading` to false and populates the `Forecasts` property, converting the incoming `IEnumerable` to an `ImmutableList`. ```csharp [ReducerMethod] public static WeatherState ReduceFetchForecastsResultAction(WeatherState state, FetchForecastsResultAction action) => new WeatherState( IsLoading: false, Forecasts: action.Forecasts?.ToImmutableList() ?? []); ``` -------------------------------- ### Optional Action Type for ReducerMethod in Fluxor Source: https://github.com/mrpmorris/fluxor/blob/master/Docs/releases.md Similar to EffectMethods, ReducerMethods can now optionally specify the action type to prevent compiler warnings when the action parameter is unused. The syntax is equivalent to explicitly including the action type. ```csharp public class SomeReducers { [ReducerMethod(typeof(IncrementCounterAction))] public MyState CallItWhateverYouLike(MyState state) { new MyState(state.Count + 1); } // is equivalent to [ReducerMethod] public MyState CallItWhateverYouLike(MyState state, IncrementCounterAction unusedParameter) { new MyState(state.Count + 1); } } ``` -------------------------------- ### Define WeatherForecast DTO Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01B-EffectsTutorial/README.md Defines the data transfer object for weather forecast information. This is typically used for communication between the client and server. ```csharp public class WeatherForecast { public DateTime Date { get; set; } public int TemperatureC { get; set; } public string Summary { get; set; } public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); } ``` -------------------------------- ### Display Counter State Value Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/02-Blazor/02A-StateActionsReducersTutorial/README.md Access and display the `ClickCount` property from the injected `CounterState` within your Blazor component's markup. ```html

Current count: @CounterState.Value.ClickCount

``` -------------------------------- ### Optional Action Type for EffectMethod in Fluxor Source: https://github.com/mrpmorris/fluxor/blob/master/Docs/releases.md Use the optional 'actionType' parameter in [EffectMethod] to avoid compiler warnings when the action is not explicitly used in the method body. This is equivalent to providing the action type as a parameter. ```csharp public class SomeEffects { [EffectMethod(typeof(RefreshDataAction))] public Task CallItWhateverYouLike(IDispatcher dispatcher) { ... code here ... } // is equivalent to [EffectMethod] public Task CallItWhateverYouLike(RefreshDataAction unusedParameter, IDispatcher dispatcher) { ... code here ... } } ``` -------------------------------- ### Unsubscribing from All Actions Source: https://github.com/mrpmorris/fluxor/blob/master/Source/Tutorials/01-BasicConcepts/01E-ActionSubscriber/README.md Implements the `IDisposable.Dispose` method to ensure all Fluxor actions are unsubscribed from when the application is disposed. This prevents memory leaks. ```csharp void IDisposable.Dispose() { // IMPORTANT: Unsubscribe to avoid memory leaks! ActionSubscriber.UnsubscribeFromAllActions(this); } ```