Fluxor를 사용한 고급 Blazor 상태 관리, 섹션 3 - 효과

32057 단어 csharpblazor

This is the third in a short series of blog posts where I will go beyond the introductory level and dig a bit deeper into using the Fluxor library in a Blazor Wasm project.


효과는요?


Fluxor 저장소는 상태, 기능, 조작, 복원, 효과 등 다섯 가지 부분으로 구성되어 있다는 것을 알아차렸을 것이다.하지만 지금까지 이 중 4건만 사용했다.우리는 아직 효과에 미치지 못했다.왜 안 해요?
할당된 작업을 수행하려면 Store 방법으로는 Reducer 이외의 리소스에 액세스할 수 없으며 Effect in Fluxor을 사용합니다.일반적인 예로는 API에 대한 HTTP 호출이 있습니다.Reducer 메서드는 현재 State과 가입한 Action에만 액세스할 수 있습니다.Effect 방법은 외부 자원을 방문하고 반대로 Actions 자체를 분배할 수 있다. 이 Reducers은 이를 처리하여 새로운 State을 보낼 것이다.Effect개의 방법을 포함하는 클래스는 실례 클래스(비정태)로 그 구조 함수를 통해 실례화할 때 자원을 주입할 수 있다.Blazor의 표준 의존항 주입 메커니즘을 사용하여 구성 요소를 주입할 수 있는 모든 것은 Effect류의 실례를 주입할 수 있다.

일기 예보


우리가 사용하고 있는 Blazor Web Assembly 위탁 관리 템플릿의 '데이터 가져오기' 구성 요소는 API 호출을 통해 일기예보 데이터를 검색하기 때문에 Effect 방법을 사용할 수 있기 때문에 우리가 해야 할 일이다.
다음 장면을 고려하십시오.

The Weather Forecasts should only be automatically retrieved the first time the page is loaded. On subsequent views, the already-retrieved forecasts should be displayed, not new ones.

New Weather Forecasts should be retrieved only when clicking a "Refresh Forecasts" button on the page.


현재 페이지를 볼 때마다 새로운 예측이 로드됩니다.

구성 요소가 초기화될 때마다(렌더링할 때마다) WeatherForecast[]이 채워지기 때문입니다.
@code {
    private WeatherForecast[] forecasts;

    protected override async Task OnInitializedAsync()
    {
        forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
    }
}

우리가 진정으로 원하는 것은 페이지를 불러올 때 예측하고 다시 표시하는 상태를 유지하고, 상태가 초기화되었는지 추적하는 방법입니다.그렇게 지도 모른다, 아마, 아마...
public record WeatherState 
{
    public bool Initialized { get; init; }
    public bool Loading { get; init; }
    public WeatherForecast[] Forecasts { get; init; }
}
이 구성 요소를 Fluxor 특성으로 바꿉니다.
첫 번째, 날씨를 클로즈업할 곳을 찾습니다.\Features\Weather 폴더를 만들고 Pages 폴더와 Store 폴더를 만듭니다.새로 만든 FetchData.razor 폴더로 Pages 폴더를 이동합니다.Store 폴더에 WeatherStore.cs 파일을 만듭니다.
WeatherStore에는 Fluxor Store의 다섯 가지 부분이 있는데 그것이 바로 상태, 기능, 동작, 복원, 효과이다.
우리는 위의 그림과 같이 날씨 상태가 있다.
이 기능은 다음과 같습니다.
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>()
        };
    }
}
행동:
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;
    }
}
감속기:
public static class WeatherReducers 
{
    [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
        };
    }
}
따라서 상단에 Fluxor 요구 사항을 추가하는 등 FetchData.razor 파일을 변경할 수 있습니다.
@inherits FluxorComponent

@using BlazorWithFluxor.Shared
@using BlazorWithFluxor.Client.Features.Weather.Store

@inject IDispatcher Dispatcher
@inject IState<WeatherState> WeatherState
@inject HttpClient Http
@code 블록을 다음과 같이 변경합니다.
@code {

    private WeatherForecast[] forecasts => WeatherState.Value.Forecasts;
    private bool loading => WeatherState.Value.Loading;

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

    private async Task LoadForecasts()
    {
        Dispatcher.Dispatch(new WeatherSetLoadingAction(true));
        var forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
        Dispatcher.Dispatch(new WeatherSetForecastsAction(forecasts));
        Dispatcher.Dispatch(new WeatherSetLoadingAction(false));
    }

}
표시에서 이 서투른 빈 검사를 교환합니다
@if (forecasts == null)
{
    <p><em>Loading...</em></p>
}
더욱 의미 있는 것은:
@if (_loading)
{
    <p><em>Loading...</em></p>
}
다시 로드 예측을 트리거하는 버튼을 추가합니다.
</table>
<br />
<button class="btn btn-outline-info" @onclick="LoadForecasts">Refresh Forecasts</button>
구축, 실행 및 실행:

그러나 이 부분은 여전히 어색하다.
private async Task LoadForecasts()
{
    Dispatcher.Dispatch(new WeatherSetLoadingAction(true));
    var forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
    Dispatcher.Dispatch(new WeatherSetForecastsAction(forecasts));
    Dispatcher.Dispatch(new WeatherSetLoadingAction(false));
}
이곳에는 대량의 국가 조종이 있는데 이것은 WeatherStore에 두기에 더욱 적합한 것 같다.우리가 진정으로 원하는 것은 이런 것이다.
private void LoadForecasts()
{
    Dispatcher.Dispatch(new WeatherLoadForecastsAction());
}
Action에서 WeatherStore의 처리 프로그램은 모든 상태 조작을 처리하고 페이지가 끊임없이 변화하는 상태에 반응하도록 한다(내가 그곳에서 한 것 참조)😉) 운전이 아니라Effect가지 방법이 작용하는 곳이다.Reducer은 API를 호출할 수 없기 때문에 Effect 방법이 필요합니다.
그러면 WeatherStore으로 돌아가서 새로운 동작을 추가합니다.
public class WeatherLoadForecastsAction { }
그리고 WeatherEffects학급과 EffectMethod학급:
public class WeatherEffects 
{
    private readonly HttpClient Http;

    public WeatherEffects(HttpClient http)
    {
        Http = http;
    }

   [EffectMethod(typeof(WeatherLoadForecastsAction))]
    public async Task LoadForecasts(IDispatcher dispatcher) 
    {
        dispatcher.Dispatch(new WeatherSetLoadingAction(true));
        var forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
        dispatcher.Dispatch(new WeatherSetForecastsAction(forecasts));
        dispatcher.Dispatch(new WeatherSetLoadingAction(false));
    }
}
그것은 WeatherReducers류와 유사하지만 매우 크고 뚜렷한 차이가 있다. 이 류는 구조 함수가 있는데 우리는 그 안에 의존항을 주입할 수 있다. 우리는 각 EffectMethod에서 그것을 사용할 수 있다.[ReducerMethod]과 유사하게 [EffectMethod]은 속성에 작업 유형 선언(작업에 유효 부하가 없는 경우)을 사용하거나 메소드 매개 변수로 선언할 수 있습니다.
[EffectMethod]
public async Task LoadForecasts(WeatherLoadForecastsAction action, IDispatcher dispatcher) 
{
    // code
}
이런 상황에서 유효 부하가 없기 때문에 우리는 속성에서 조작을 성명합니다.
이제 우리는 FetchData.razor 파일로 돌아가서 @inject HttpClient Http 파일을 삭제할 수 있다. 왜냐하면 우리는 더 이상 그것을 필요로 하지 않기 때문이다. 페이지의 @code 블록은 지금 보기에 다음과 같다.
@code {

    private WeatherForecast[] forecasts => WeatherState.Value.Forecasts;
    private bool loading => WeatherState.Value.Loading;

    protected override void OnInitialized()
    {
        if (WeatherState.Value.Initialized == false)
        {
            LoadForecasts();
            Dispatcher.Dispatch(new WeatherSetInitializedAction());
        }
        base.OnInitialized();
    }

    private void LoadForecasts()
    {
        Dispatcher.Dispatch(new WeatherLoadForecastsAction());
    }

}
구축, 운행, 우리는 여전히 위와 같은 행위를 보아야 한다.구성 요소를 초기화할 때 예측을 불러옵니다. 구성 요소는 보기 사이에서 상태를 유지하고 '새로 고침 예측' 단추를 눌렀을 때 예측을 다시 불러옵니다.WeatherEffects반에 대해서...코드를 이동하는 것 외에 우리는 또 무엇을 얻었습니까?이 시리즈의 다음 부분에서 우리는 WeatherLoadForecastsAction의'부하 예측'기능이 응용 프로그램 구성 요소 간의 더욱 복잡한 상호작용에 어떻게 기회를 열 수 있는지 연구할 것이다.
이 모든 단계를 완료하면 솔루션이 this branch of the demo repository처럼 보일 것입니다.
도움이 되거나 건의가 있거나 물어보고 싶으면 댓글을 남겨주세요.
즐거운 코딩!

Edit: Peter, in his comment below, suggests an adjustment to the Store that simplifies a few things. Here is what the Actions/Reducers/Effects would look like after the changes:


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

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

    [ReducerMethod(typeof(WeatherLoadForecastsAction))]
    public static WeatherState OnLoadForecasts(WeatherState state)
    {
        return state with
        {
            Loading = true
        };
    }
}

public class WeatherEffects 
{
    private readonly HttpClient Http;
    private readonly IState<CounterState> CounterState;

    public WeatherEffects(HttpClient http, IState<CounterState> counterState)
    {
        Http = http;
        CounterState = counterState;
    }

    [EffectMethod(typeof(WeatherLoadForecastsAction))]
    public async Task LoadForecasts(IDispatcher dispatcher) 
    {
        var forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("WeatherForecast");
        dispatcher.Dispatch(new WeatherSetForecastsAction(forecasts));
    }
}

#region WeatherActions
public class WeatherSetInitializedAction { }
public class WeatherLoadForecastsAction { }
public class WeatherSetForecastsAction
{
    public WeatherForecast[] Forecasts { get; }

    public WeatherSetForecastsAction(WeatherForecast[] forecasts)
    {
        Forecasts = forecasts;
    }
}
#endregion

이로써 LoadForecasts EffectMethod를 간소화하고 Loading 상태의 업데이트를 WeatherLoadForecastsActionWeatherSetForecastsAction을 직접 구독하는 복원 프로그램으로 이동하여 이 effect가 추가 조작을 보내는 수요를 없앨 수 있다.

좋은 웹페이지 즐겨찾기