Я новичок в fluxor и шаблонах redux / flux, на которых он основан. В настоящее время я работаю над wasm-проектом Blazor. Я решил использовать fluxor для управления состоянием. Но мне интересно, как справиться со следующим сценарием:

Когда страница загружается, компонент 1 заполняется данными из API через управление состоянием fluxor.

Компонент 1 - это список с элементами, которые может выбрать пользователь. При щелчке указанный элемент должен быть получен из API и полностью показан внутри компонента 3. А текст, представляющий активный элемент, отображается в компоненте 2.

Компонент 2 - это навигатор, который перемещается по списку с помощью простых кнопок «Назад» и «Далее». При нажатии кнопки «Далее» компонент 3 должен получить следующий элемент из API и отобразить его полностью. Компонент 1 затем отражает это изменение, показывая, какой элемент списка выбран.

skribbled

У меня есть действия и т. Д., Чтобы получить полную версию. Я просто не знаю, откуда его отправлять. Или как убедиться, что все компоненты знают, что является активным элементом. С "чистым" блейзером я бы сделал все это через обратные вызовы событий и обработал бы активное состояние элемента локально. но это уничтожило бы точку флюксора.

2
Tom Esendam 15 Июн 2020 в 23:05

1 ответ

Лучший ответ

Если все эти компоненты являются разными частями одного варианта использования (для просмотра данных клиента), я бы поместил их все в одну функцию.

Допустим, у вас есть следующие классы контрактов, поступающие из вашего API.

public class ClientSummary
{
  public Guid ID { get; set; }
  public string Name { get; set; }
}

public class ClientDetails
{
  public Guid ID { get; set; }
  public string Name { get; set; }
  public string LotsOfOtherInfo { get; set; }
  public string ArchivedInfo { get; set; }  
}

Примечание . Лично я бы дал им частные сеттеры и использовал Newtonsoft для десериализации ответов API. Это гарантирует, что они неизменяемы, и вы можете использовать их непосредственно в состоянии без необходимости создавать классы-близнецы только для того, чтобы сделать ваше состояние неизменным.

Состояние вашей функции может выглядеть примерно так

public class MyUseCaseState
{
  public bool IsLoadingList { get; }
  public ReadOnlyCollection<ClientSummary> Summaries { get; }
  public int SelectedClientIndex { get; }

  public bool IsLoadingDetails { get; }
  public ClientDetails ClientDetails { get; }

  public MyUseCaseState(
    bool isLoadingList, 
    IEnumerable<ClientSummary> summaries, 
    int selectedClientIndex, 
    bool isLoadingDetails, 
    ClientDetails clientDetails)
  {
    IsLoadingList = isLoadingList;
    Summaries = (summaries ?? Enumerable.Empty<ClientSummary>()).ToList().AsReadOnly();
    SelectedClientIndex = selectedClientIndex;
    IsLoadingDetails = isLoadingDetails;
    ClientDetails = clientDetails;
  }
}

Действие, которое вы запускаете, когда отображается ваша страница, не требует никакой полезной нагрузки.

public class LoadClientListAction {}

Редуктор очистит список и установит индекс на -1;

[ReducerMethod]
public static MyUseCaseState ReduceLoadClientListAction(MyUseCaseState state, LoadClientListAction action)
=> new MyUseCaseState(
     isLoadingList: true,
     summaries: null,
     selectedClientIndex: -1,
     isLoadingDetails: false,
     clientDetails: null
   );

Ваш эффект будет отправлен на сервер, чтобы захватить список, а затем перейти в состояние с помощью действия

[EffectMethod]
public async Task HandleLoadClientListAction(LoadClientListAction action, IDispatcher dispatcher)
{
  ClientSummary[] clientSummaries = await GetFromApi.....;
  ClientSummary firstClient = clientSummaries.FirstOrDefault();
  var result = new LoadClientListResultAction(clientSummaries, firstClient);
  dispatcher.Dispatch(result);
}

Ваш метод редуктора для этого действия

[ReducerMethod]
public static MyUseCaseState ReduceLoadClientListResultAction(MyUseCaseState state, LoadClientListResultAction action)
=> new MyUseCaseState(
     isLoadingList: false,
     summaries: action.Summaries,
     selectedClientIndex: action.Summaries.Count == 0 ? -1 : 0,
     isLoadingDetails: false,
     clientDetails: action.FirstClient
   );

Теперь вам нужно загружать данные для выбранного клиента при изменении SelectedClientIndex или при загрузке списка. У вас может быть одно действие, чтобы установить выбранный индекс.

@inject IState<MyUseCaseState> MyUseCaseState

Dispatcher.Dispatcher(new SelectIndexAction(MyUseCaseState.Value.SelectedClientIndex + 1));

Теперь вы можете установить для IsLoadingDetails значение true, использовать эффект для получения данных с сервера (ClientDetails), а затем отправить результат с сервера для обновления вашего состояния.

[ReducerMethod]
public static MyUseCaseState ReduceSelectIndexAction(MyUseCaseState state, SelectIndexAction action)
=> new MyUseCaseState(
     isLoadingList: state.IsLoadingList,
     summaries: state.Summaries,
     selectedClientIndex: action.SelectedIndex,
     isLoadingDetails: true,
     clientDetails: null
   );

Эффект на получение данных

[EffectMethod]
public async Task HandleSelectIndexAction(SelectIndexAction action, IDispatcher dispatcher)
{
  ClientDetails details = await GetFromYourApi....
  var result = new SelectIndexResultAction(details);
  dispatcher.Dispatch(result);
}

Затем, наконец, обновите свое состояние

[ReducerMethod]
public static MyUseCaseState ReduceSelectIndexAction(MyUseCaseState state, SelectIndexResultAction action)
=> new MyUseCaseState(
     isLoadingList: state.IsLoadingList,
     summaries: state.Summaries,
     selectedClientIndex: state.SelectedIndex,
     isLoadingDetails: false,
     clientDetails: action.ClientDetails
   );
2
Peter Morris 16 Июн 2020 в 08:44