Fluxor Documentation

repository·master·Indexed 23 days ago

https://github.com/mrpmorris/fluxor

A zero-boilerplate Flux/Redux implementation for Microsoft .NET designed for multi-UI, single-state store front-end development. Fluxor provides a predictable state management architecture using read-only state, actions, reducers, and effects for handling side effects. It includes specific integrations for Blazor, support for Redux Dev Tools, and a middleware system for cross-cutting concerns.

Tokens
11.3K
Snippets
27
Records
41
Agent score
80%

What's inside Fluxor

  1. What is Middleware in Fluxor and how does it work?

    master

    Middleware allows you to hook into various execution points in the Store's lifetime. It enables performing additional actions across the store regardless of which action is dispatched or which states are affected.

    Common use cases include:

    • Routing Middleware: Ensuring navigation events in Blazor Web UI have corresponding actions for reducers.
    • Redux Dev Tools Middleware: Integrating with browser plugins to track state history and allow time-travel debugging.

    You can implement middleware by descending from the Fluxor.Middleware class (recommended) or by implementing the Fluxor.IMiddleware interface.

  2. What is IActionSubscriber and when to use it

    master

    IActionSubscriber allows you to subscribe to the Fluxor dispatch pipeline and receive notifications whenever a specific action is dispatched.

    Common use cases include:

    • Handling mutable data: Retrieving mutable objects from an API server that you want to edit in your application without storing them in the immutable Fluxor state.
    • UI Synchronization: Notifying the UI that a specific asynchronous action (like CustomerSearchAction) has completed so the interface can perform side effects, such as setting focus to a specific control.
  3. What is Middleware in Fluxor?

    master

    Middleware allows you to hook into various execution points in the Store's lifetime. It enables performing additional actions across the store regardless of which action is dispatched or which states are affected.

    Common use cases include:

    • Routing Middleware: Ensuring navigation events in Blazor Web UI have corresponding actions for reducers.
    • Redux Dev Tools Middleware: Integrating with browser extensions to track state history.
    • Logging: Monitoring dispatched actions and state changes.
  4. Understand the Flux pattern in Fluxor

    master

    Fluxor implements the Flux pattern, which is a predictable state management architecture. To use Fluxor correctly, you must follow these core rules:

    1. Read-only State: The application state should never be mutated directly; it must always be treated as read-only.
    2. Dispatch Actions: To request a change to the state, the application must dispatch an Action.
    3. Reducers for State Transitions: Reducers process dispatched actions. A reducer takes the current state and the action, then creates a new state object that combines the old state with the changes requested by the action.
    4. UI Updates: The UI observes the state and re-renders itself whenever a new state is produced.
  5. Middleware Lifecycle Methods

    master

    When implementing a class derived from Fluxor.Middleware, you can override the following methods to hook into the store lifecycle:

    1. Task InitializeAsync(IDispatcher dispatcher, IStore store): Executed when the store is first initialized. Use this to capture a reference to the IStore.
    2. void AfterInitializeAllMiddlewares(): Executed once the store has been initialized and InitializeAsync has been completed for all registered middleware.
    3. bool MayDispatchAction(object action): Called every time IDispatcher.Dispatch is executed. Every middleware gets a chance to veto the action. If it returns false, the dispatch process is terminated for that action. The first middleware to return false stops the chain.
    4. void BeforeDispatch(object action): Called after all middlewares have approved the action, but before the action is reduced into state.
    5. void AfterDispatch(object action): Called after the action has been processed by all reducers.
  6. What are Effects and when to use them

    master

    In Fluxor, state is immutable and should only be updated by pure functions (Reducers). However, real-world applications often need to interact with external data sources like web services.

    Effects are the mechanism used to handle side effects. They allow you to perform asynchronous tasks (like API calls) in response to an action. An Effect cannot modify state directly; instead, it performs its task and then dispatches a new action containing the result, which a Reducer then uses to update the state.

  7. How Effects work in Fluxor

    master

    In Fluxor, state must remain immutable and is only updated by pure functions (Reducers). To interact with external data sources like web services, you use Effects.

    An Effect is triggered when a specific action is dispatched. Instead of modifying state directly, an Effect performs side effects (like an HTTP call) and then dispatches a new action containing the result. This new action is then handled by a Reducer to update the state.

    The Workflow:

    1. UI dispatches an initial action (e.g., FetchDataAction).
    2. Reducers catch the action to update the state (e.g., setting IsLoading = true).
    3. Effects catch the same action to perform the side effect (e.g., calling a server).
    4. Effect dispatches a result action (e.g., FetchDataResultAction) once the side effect completes.
    5. Reducers catch the result action to update the state with the new data (e.g., setting IsLoading = false and populating the list).
    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
  8. Implement Reducers to update State

    master

    Reducers are responsible for taking the current state and an action, and returning a new state instance.

    Best Practices:

    • Use static methods decorated with [ReducerMethod].
    • Reducers should be pure functions; avoid injecting dependencies into them. If you need side effects or dependencies, use an Effect instead.
    • You can split reducer methods across multiple static classes; Fluxor will find them via assembly scanning.

    Handling unused parameters: If a reducer method receives an action but doesn't use its properties, you can avoid compiler warnings by specifying the action type in the attribute: [ReducerMethod(typeof(MyAction))].

    Alternative Pattern: You can inherit from Reducer<TState, TAction>, but this is generally not recommended as it requires more boilerplate than static methods.

    public static class Reducers
    {
      [ReducerMethod]
      public static CounterState ReduceIncrementCounterAction(CounterState state, IncrementCounterAction action) =>
        new CounterState(clickCount: state.ClickCount + 1);
    }
    
    // Reducer avoiding unused parameter warning
    [ReducerMethod(typeof(IncrementCounterAction))]
    public static CounterState ReduceIncrementCounterAction(CounterState state) =>
      new CounterState(clickCount: state.ClickCount + 1);
    
    // Alternative (not recommended) inheritance pattern
    public class IncrementCounterReducer : Reducer<CounterState, IncrementCounterAction>
    {
      public override CounterState Reduce(CounterState state, IncrementCounterAction action) =>
        new CounterState(clickCount: state.ClickCount + 1);
    }
  9. Install and initialize Fluxor.Blazor.Web

    master

    To use Fluxor in a Blazor application, follow these steps:

    1. Add NuGet Package: Add Fluxor.Blazor.Web to your project.
    2. Register Fluxor: In your Program.cs, register the services using AddFluxor. You must provide an assembly to scan for states, actions, and reducers using ScanAssemblies.
      • If using a Blazor WebAssembly Standalone app, perform this in the main project.
      • If using a Blazor Web App with both {App}.csproj and {App}.Client.csproj, you must call AddFluxor in the Program.cs of both projects. It is recommended to use a shared static method for registration.
    3. Initialize the Store: Add the <Fluxor.Blazor.Web.StoreInitializer/> component to your markup to ensure the store is initialized.
      • Blazor Wasm Standalone: Place it in App.razor.
      • All other apps: Place it in Routes.razor (above the <Router> component).
    // In Program.cs
    builder.Services.AddFluxor(x =>
        x.ScanAssemblies(typeof({SomeType}).Assembly));
    <!-- In App.razor or Routes.razor -->
    <Fluxor.Blazor.Web.StoreInitializer/>
  10. Create and Register a Middleware plugin

    master

    To create a middleware, descend from Fluxor.Middleware. To register it, use the AddMiddleware<T> method within the AddFluxor configuration in your Program class.

    Example implementation of a basic middleware that captures the store reference:

    public class LoggingMiddleware : Middleware
    {
      private IStore Store;
    
      public override Task InitializeAsync(IDispatcher dispatcher, IStore store)
      {
        Store = store;
        Console.WriteLine(nameof(InitializeAsync));
        return Task.CompletedTask;
      }
    }
    
    // Registration in Program.cs
    services.AddFluxor(o => o
      .ScanAssemblies(typeof(Program).Assembly)
      .AddMiddleware<LoggingMiddleware>());
  11. Display Fluxor state in a Blazor component

    master

    To use Fluxor state in a Blazor component, follow these steps:

    1. Inherit from FluxorComponent to ensure the component re-renders when the state changes.
    2. Inject IState<TState> to access the current state value.
    3. Access the state via the .Value property.

    Example:

    @inherits Fluxor.Blazor.Web.Components.FluxorComponent
    @using YourNamespace.Store.WeatherFeature
    @inject IState<WeatherState> WeatherState
    
    @if (WeatherState.Value.IsLoading)
    {
        <p>Loading...</p>
    }
    else
    {
        @foreach (var forecast in WeatherState.Value.Forecasts)
        {
            <p>@forecast.Summary</p>
        }
    }
    @inherits Fluxor.Blazor.Web.Components.FluxorComponent
    @using FluxorBlazorWeb.EffectsTutorial.Store.WeatherFeature
    @inject IDispatcher Dispatcher
    @inject IState<WeatherState> WeatherState
    
    @code {
        protected override void OnInitialized()
        {
            base.OnInitialized();
            var action = new FetchForecastsAction();
            Dispatcher.Dispatch(action);
        }
    }
  12. Learn Fluxor with Blazor for web development

    master

    If you are building web applications with Blazor, Fluxor provides specific patterns and integrations. Tutorials are available for:

    • State, Actions, and Reducers in Blazor
    • Effects in Blazor
    • Middleware in Blazor
    • Redux Dev Tools: Integration to use browser-based debugging tools to inspect your Flux state transitions.