В моем проекте TypeScript я предоставляю пользователю диалог ветвления.Отображается текст, и пользователю предоставляются параметры, которые затем вводят новую ветвь.С каждой веткой может быть связан один или несколько Action
, которые не имеют ничего общего с Redux.Мы назовем эти MyAction
s для ясности.
MyAction
s довольно универсальны, вы можете выбрать тип действия и аргументы для передачи вместе с ним.Действие соответствует функции, зарегистрированной в ActionParser
, которая отвечает за выполнение этих MyAction
s.
Одним из наиболее важных MyAction
s является действие LOAD_SCENE
, которое позволяетПользователь переключается на совершенно другую сцену.Сцена сначала загружается с сервера, а затем представляется пользователю в корневой ветви дерева диалога.Именно здесь начинаются проблемы.
Обычно сцена загружается явно в результате нажатия пользователем кнопки в пользовательском интерфейсе.Отправляется Thunk, сцена загружается, анализируется, а затем отправляется действие, информирующее нас об успешной загрузке:
Это происходит следующим образом:
- Пользователь щелкает[button] в пользовательском интерфейсе
- Кнопка запускает mapDispatchToProps.onButtonClick
- onButtonClick вызывает функцию, которая обновляет SceneParser с новой ветвью.
- С выбранной новой ветвью отправляется onButtonClickвызывается действие PARSING_START
- SceneParser.parseActiveBranch, в котором выполняются действия.Здесь происходят побочные эффекты.
- Наконец, вызывается PARSING_SUCCESS и вызывается SCENE_UPDATE для отображения нового диалога и параметров
На шаге 5 выполняются действия.Действие LOAD_SCENE выглядит следующим образом:
ActionParser.register("LOAD_SCENE", async (args) => {
if (args.scene) {
Store.get().dispatch(loadSceneStart());
await SceneManager.loadScene(args.scene);
Store.get().dispatch(loadSceneSuccess(SceneManager.activeScene.author));
} else {
throw new Error("action 'LOAD_SCENE' requires 'scene' argument");
}
});
Проблемы очевидны:
- Пользовательский интерфейс сцены не обновляется - я мог бы легко решить это, добавив это здесь, но этоможет также привести к непреднамеренным побочным эффектам.
- Я напрямую получаю доступ к магазину для отправки, я не знаю, считается ли это хорошей практикой, но это определенно кажется неправильным.
MyAction
s всегда запускаются как часть бизнес-логики.Поскольку они могут иметь побочные эффекты, я хочу, чтобы результаты моих действий влияли на состояние, но в то же время я не хочу отправлять их из действия.Вот функция dispatch
, которая делает все это.
public static onSelectOption(dispatch: Dispatch, branch: string) {
try {
SceneManager.selectBranch(branch); // MyActions are processed here. Side effects of MyActions may have happened.
// These fire immediately because some MyActions may be asynchronous.
if (SceneManager.sceneIsFinished) {
dispatch(setMode(Mode.MainMenu));
} else {
dispatch(updateScene(SceneManager.dialogue, SceneManager.options));
}
} catch (error) {
dispatch(setGameView(View.Error));
dispatch(setError(error.stack || error.message));
}
}
Как лучше всего справляться с побочными эффектами?Как лучше всего обрабатывать изменения, чтобы отразить их в пользовательском интерфейсе и состоянии?Чтобы быть понятным, мой код работает, но я чувствую, что делаю это неправильно.