blazorstate.net-8.0fluxor

Fluxor state no change in time in .NET 8


In Fluxor [github page](Fluxor/Source/Tutorials/02-Blazor/02A-StateActionsReducersTutorial at master · mrpmorris/Fluxor (github.com)):

@inherits Fluxor.Blazor.Web.Components.FluxorComponent

Note: this is required to ensure the component re-renders whenever its state changes. When you inherit from FluxorComponent the override of OnInitialized() will override the implementation of the FluxorComponent. Make sure you call base.OnInitialized(); to register the StateHasChanged events correctly and to be notified about StateChanges. The override of OnInitializedAsync() will still override the ComponentBase implementation because Fluxor does not implement it on its own. An example for an OnInitialized() method when you are using

@inherits Fluxor.Blazor.Web.Components.FluxorComponent 

would look like:

protected override void OnInitialized()
{
    base.OnInitialized();
    Dispatcher.Dispatch(new FetchDataAction());
}

Counter.razor:

@inherits Fluxor.Blazor.Web.Components.FluxorComponent 

<h1>Counter</h1>

<p>Current count1: @CounterState.Value.ClickCount</p>

<button class="btn btn-primary" @onclick="@(() => IncrementCount(5))">Click me (+5)</button>

Home.razor:

@inherits Fluxor.Blazor.Web.Components.FluxorComponent

@using Fluxor
@using Microsoft.AspNetCore.Components;
@using FluxorBlazorWeb.StateActionsReducersTutorial.Store.CounterUseCase

@code {
    [Inject]
    private IState<CounterState> CounterState { get; set; } 
}

<h1>Counter</h1>

<p>Current count1: @CounterState.Value.ClickCount</p> // after Counter IncrementCount click, this value not change in time, only refresh, it should be change. how to do?
<Counter /> 

When counter IncrementCount is clicked, this value not change in parent in time, and Counter component CountStatevalue change display, only refresh, it should be change. How to do? But github code is .NET 6, it can be display in normal.

I want to click children Counter button, the state change in time, and the state display in parent page also, like this:

Routes.razor

<Fluxor.Blazor.Web.StoreInitializer />
<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)"/>
        <FocusOnNavigate RouteData="@routeData" Selector="h1"/>
    </Found>
</Router>

CounterState.cs

using Fluxor;

namespace BlazorApp_Fluxor.Stores.CounterState
{
    [FeatureState]
    public class CounterState
    {
        public int ClickCount { get; }

        public CounterState() { }
        public CounterState(int clickCount)
        {
            ClickCount = clickCount;
        }
    }
}

IncrementCounterAction.cs

namespace BlazorApp_Fluxor.Stores.CounterState
{
    public class IncrementCounterAction
    {
        public int Increment { get; }

        public IncrementCounterAction(int increment)
        {
            Increment = increment;
        }
    }

    public class DecrementCounterAction
    {
        public int Decrement { get; }

        public DecrementCounterAction(int decrement)
        {
            Decrement = decrement;
        }
    }
}

Reducers.cs

using Fluxor;

namespace BlazorApp_Fluxor.Stores.CounterState
{
    public static class Reducers
    {
        [ReducerMethod]
        public static CounterState Reduce(CounterState state, IncrementCounterAction action)
        {
            return new CounterState(state.ClickCount + action.Increment);
        }

        [ReducerMethod]
        public static CounterState Reduce(CounterState state, DecrementCounterAction action)
        {
            return new CounterState(state.ClickCount - action.Decrement);
        }
    }
}

Solution

  • Here is a working sample for you.

    Home.razor

    @page "/"
    @inherits Fluxor.Blazor.Web.Components.FluxorComponent
    @using Fluxor
    @using FluxorInBlazor.State.Counter
    
    <PageTitle>Home</PageTitle>
    
    <h1>Hello, world!</h1>
    
    <p>Current count in Home: @CounterState.Value.ClickCount</p>
    
    <Counter />
    
    @code {
        [Inject]
    
        private IState<CounterState> CounterState { get; set; }
    }
    

    Counter.razor

    @page "/counter"
    @using FluxorInBlazor.State.Counter
    @using Fluxor
    @using FluxorInBlazor.State.Counter.Actions
    @inherits Fluxor.Blazor.Web.Components.FluxorComponent
    @inject IState<CounterState> CounterState
    @inject IDispatcher Dispatcher
    <PageTitle>Counter</PageTitle>
    
    <h1>Counter</h1>
    
    <p role="status">Current count: @CounterState.Value.ClickCount</p>
    
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
    
    @code {
        private void IncrementCount()
        {
            Dispatcher.Dispatch(new IncrementCounterAction());
        }
    
    }
    

    IncrementCounterAction.cs

    namespace FluxorInBlazor.State.Counter.Actions;
    
    public record IncrementCounterAction;
    

    CounterState.cs

    using Fluxor;
    
    namespace FluxorInBlazor.State.Counter;
    
    [FeatureState]
    public record CounterState
    {
        public int ClickCount { get; init; }
    }
    

    Reducers.cs

    using Fluxor;
    using FluxorInBlazor.State.Counter.Actions;
    
    namespace FluxorInBlazor.State.Counter;
    
    public static class Reducers
    {
        [ReducerMethod]
        public static CounterState ReduceIncrementCounterAction(CounterState state, IncrementCounterAction action)
        {
            return new() {ClickCount = state.ClickCount + 1};
        }
    }
    

    LoggingMiddleware.cs

    using Fluxor;
    
    namespace FluxorInBlazor.State.Middlewares;
    
    public class LoggingMiddleware : Middleware
    {
        public override void BeforeDispatch(object action)
        {
            Console.WriteLine($"Before dispatching action {action.GetType().Name}");
        }
    }
    

    App.razor

    <Fluxor.Blazor.Web.StoreInitializer/>
    <Router AppAssembly="@typeof(App).Assembly">
        <Found Context="routeData">
            <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"/>
            <FocusOnNavigate RouteData="@routeData" Selector="h1"/>
        </Found>
        <NotFound>
            <PageTitle>Not found</PageTitle>
            <LayoutView Layout="@typeof(MainLayout)">
                <p role="alert">Sorry, there's nothing at this address.</p>
            </LayoutView>
        </NotFound>
    </Router>
    

    Program.cs

    using Fluxor;
    using Microsoft.AspNetCore.Components.Web;
    using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
    using FluxorInBlazor;
    using FluxorInBlazor.State.Middlewares;
    
    var builder = WebAssemblyHostBuilder.CreateDefault(args);
    builder.RootComponents.Add<App>("#app");
    builder.RootComponents.Add<HeadOutlet>("head::after");
    
    builder.Services.AddScoped(sp => new HttpClient {BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)});
    builder.Services.AddFluxor(options => options.ScanAssemblies(typeof(Program).Assembly)
        .AddMiddleware<LoggingMiddleware>());
    
    await builder.Build().RunAsync();
    

    My Sample Structure

    enter image description here

    Test Result

    enter image description here