[Blazor] Fluxor - Effects

Effect란?

  • Blazor Stores 에서는 State, Feature, Actions, Reducers 및 Effects 의 총 5가지 부분으로 구성되어 있습니다.
  • 여기서 오늘은 Fluxor 에서 Effects 가 무엇인지에 대해서 알아보려고 합니다.
  • Fluxor 에서 Effects 는 Dispatch 된 Action 이 순수하게 Reducer 방법으로 Store 외부의 리소스에 접근해야 할 때 사용됩니다.
  • 일반적인 예는 API 에 HTTP 객체를 호출하는 것입니다.
  • Reducer 메서드는 현재 상태 및 구독 중인 Action 만 접근할 수 있습니다.
  • Effect 메서드는 외부 리소스에 액세스할 수 있으며, 새 상태를 방출하기 위해 Reducers에서 처리할 Acts 자체를 디스패치합니다.
  • Effect 메서드를 포함하는 클래스는 인스턴스화 시 생성자를 통해 리소스를 주입할 수 있는 인스턴스 클래스(비정적)입니다.
  • Blazor의 표준 종속성 주입 메커니즘을 사용하여 구성 요소에 주입할 수 있는 모든 것을 Effect 클래스 인스턴스에 주입할 수 있습니다.

예제 코드

  • Effect 예제 코드는 Blazor Server 기본 프로젝트를 생성하게 되면 자동 생성되는 razor 중 하나인 일기예보 페이지를 대상으로 Effects 예제를 작성해 봅니다.
  • 현재 Fluxor 를 도입하기 전에는 Fetch data 메뉴에서 다른 메뉴를 갔다가 돌아오면, 데이터가 Reset 되어 새로운 데이터가 표시되는 것을 확인할 수 있습니다.
  • Fluxor 를 이용하여 상태를 유지하고, Reset 버튼을 클릭할 때 새로운 데이터로 갱신되도록 코드 작성을 진행해 보겠습니다.

1. 상태 클래스 추가

  • 그럼 이제 Fluxor 기능으로 하나씩 바꾸어 보도록 하겠습니다.
  • 먼저, 날씨 정보 기능을 위한 State 클래스를 생성합니다.
using FluxExample.Data;
using Fluxor;

namespace FluxExample.Store.Weather;

public record WeatherState
{
    public bool Initialized { get; init; }
    public bool Loading { get; init; }
    public WeatherForecast[]? Forecasts { get; init; }
}

public class WeatherFeature : Feature<WeatherState>
{
    public override string GetName() => "Weather";

    protected override WeatherState GetInitialState()
    {
        return new WeatherState
        {
            Initialized = false,
            Loading = false,
            Forecasts = Array.Empty<WeatherForecast>()
        };
    }
}

2. Action 클래스 추가

using FluxExample.Data;
using Fluxor;

namespace FluxExample.Store.Weather
{
    public class WeatherSetInitializedAction
    {

    }

    public class WeatherSetForecastsAction
    {
        public WeatherForecast[] Forecasts { get; }
        public WeatherSetForecastsAction(WeatherForecast[] forecasts)
        {
            Forecasts = forecasts;
        }
    }

    public class WeatherSetLoadingAction
    {
        public bool Loading { get; }

        public WeatherSetLoadingAction(bool loading)
        {
            Loading = loading;
        }
    }
}

3. Reducer 클래스 추가

using Fluxor;

namespace FluxExample.Store.Weather;

public static class Reducers
{
    [ReducerMethod]
    public static WeatherState OnSetForecasts(WeatherState state, WeatherSetForecastsAction action)
    {
        return state with
        {
            Forecasts = action.Forecasts
        };
    }

    [ReducerMethod]
    public static WeatherState OnSetLoading(WeatherState state, WeatherSetLoadingAction action)
    {
        return state with
        {
            Loading = action.Loading
        };
    }

    [ReducerMethod(typeof(WeatherSetInitializedAction))]
    public static WeatherState OnSetInitialized(WeatherState state)
    {
        return state with
        {
            Initialized = true
        };
    }
}

4. Effect 추가

  • 다음으로, Effects 를 추가하는 내용입니다.
  • 앞서, OnSetForecasts, OnSetLoading, OnSetInitialized 총 3개의 Reducer 를 추가하였습니다.
  • 해당 Reducer 는 FetchData 화면에서 Reset 버튼을 클릭할 시, 새롭게 Weather 데이터를 갱신하는 내용을 담고 있습니다.
  • 이 내용을 바로 razor 페이지에서 사용할 수도 있지만, Effects 로 묶어서 한번에 호출을 할 수도 있습니다.
using FluxExample.Data;
using Fluxor;

namespace FluxExample.Store.Weather
{
    public class WeatherLoadForecastsAction { }

    public class WeatherEffects
    {
        private readonly WeatherForecastService ForecastService;

        public WeatherEffects(WeatherForecastService rorecastService)
        {
            ForecastService = rorecastService;
        }

        [EffectMethod(typeof(WeatherLoadForecastsAction))]
        public async Task LoadForecasts(IDispatcher dispatcher)
        {
            dispatcher.Dispatch(new WeatherSetLoadingAction(true));
            var forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
            dispatcher.Dispatch(new WeatherSetForecastsAction(forecasts));
            dispatcher.Dispatch(new WeatherSetLoadingAction(false));
        }
    }
}

5. FetchData.razor 코드

  • 그럼 이제, FetchData.razor 파일에 Fluxor 를 사용하는 코드를 추가해 주면 됩니다.
@page "/fetchdata"

<PageTitle>Weather forecast</PageTitle>

@using FluxExample.Data
@using FluxExample.Store.Weather
@using static System.Net.WebRequestMethods

<h1>Weather forecast</h1>

<p>This component demonstrates fetching data from a service.</p>

@if (loading)
{
    <p><em>Loading...</em></p>
}
else
{
    <table class="table">
        <thead>
            <tr>
                <th>Date</th>
                <th>Temp. (C)</th>
                <th>Temp. (F)</th>
                <th>Summary</th>
            </tr>
        </thead>
        <tbody>
            @foreach (var forecast in forecasts)
            {
                <tr>
                    <td>@forecast.Date.ToShortDateString()</td>
                    <td>@forecast.TemperatureC</td>
                    <td>@forecast.TemperatureF</td>
                    <td>@forecast.Summary</td>
                </tr>
            }
        </tbody>
    </table>
    <br/>
    <button class="btn btn-outline-info" @onclick="LoadForecasts">Refresh Forecasts</button>
}

@code {
    [Inject]
    public IDispatcher? Dispatcher { get; set; }

    [Inject]
    public IState<WeatherState>? State { get; set; }

    private WeatherForecast[]? forecasts => State!.Value.Forecasts;
    private bool loading => State!.Value.Loading;

    protected override async Task OnInitializedAsync()
    {
        if (State!.Value.Initialized == false)
        {
            await LoadForecasts();
            Dispatcher!.Dispatch(new WeatherSetInitializedAction());
        }
        await base.OnInitializedAsync();
    }

    private async Task LoadForecasts()
    {
        Dispatcher!.Dispatch(new WeatherLoadForecastsAction());
    }
}
728x90

이 글을 공유하기

댓글

Designed by JB FACTORY