【发布时间】:2016-02-18 05:29:13
【问题描述】:
背景
一段时间以来,我一直在思考如何通过服务器交互(通过 ajax)在 Redux 中实现撤消/重做。
我提出了一个使用command pattern 的解决方案,其中操作通过execute 和undo 方法注册为命令,而不是分派操作,而是分派命令。然后将命令存储在堆栈中,并在需要时引发新的操作。
我当前的实现使用中间件来拦截调度,测试命令和调用命令的方法,看起来像这样:
中间件
let commands = [];
function undoMiddleware({ dispatch, getState }) {
return function (next) {
return function (action) {
if (action instanceof Command) {
// Execute the command
const promise = action.execute(action.value);
commands.push(action);
return promise(dispatch, getState);
} else {
if (action.type === UNDO) {
// Call the previous commands undo method
const command = commands.pop();
const promise = command.undo(command.value);
return promise(dispatch, getState);
} else {
return next(action);
}
}
};
};
}
动作
const UNDO = 'UNDO';
function undo() {
return {
type: UNDO
}
}
function add(value) {
return (dispatch, getState) => {
const { counter } = getState();
const newValue = counter + value;
return new Promise((resolve, reject) => {
resolve(newValue); // Ajax call goes here
}).then((data) => {
dispatch(receiveUpdate(data));
});
}
}
function sub(value) {
return (dispatch, getState) => {
const { counter } = getState();
const newValue = counter - value;
return new Promise((resolve, reject) => {
resolve(newValue); // Ajax call goes here
}).then((data) => {
dispatch(receiveUpdate(data));
});
}
}
命令
class Command {
execute() {
throw new Error('Not Implemented');
}
undo() {
throw new Error('Not Implemented');
}
}
class AddCommand extends Command {
constructor(value) {
super();
this.value = value;
}
execute() {
return add(this.value);
}
undo() {
return sub(this.value);
}
}
应用
const store = createStoreWithMiddleware(appReducer);
store.dispatch(new AddCommand(10)); // counter = 10
store.dispatch(new AddCommand(5)); // counter = 15
// Some time later
store.dispatch(undo()); // counter = 10
(更完整的例子here)
我发现我目前的方法存在几个问题:
- 由于通过中间件实现,整个应用程序可能只存在一个堆栈。
- 无法自定义
UNDO命令类型。 - 创建一个命令来调用操作,然后返回承诺似乎非常复杂。
- 在操作完成之前将命令添加到堆栈中。错误会发生什么?
- 由于命令未处于状态,无法添加 is_undoable 功能。
- 您将如何实施乐观更新?
帮助
那么我的问题是,谁能提出在 Redux 中实现此功能的更好方法?
我现在看到的最大缺陷是在操作完成之前添加的命令,以及添加乐观更新的难度。
感谢任何见解。
【问题讨论】:
-
我确实有(不错的小图书馆)。我可以看到如何使用它来帮助解决我的乐观问题,但就像 redux-undo 一样,它只处理减速器内部的反转操作。对于异步撤消,我正在努力将命令堆栈存储在状态中的位置和方式,因为这似乎是它在此类应用程序中存在的最合乎逻辑的位置。
-
似乎经常吹捧的说法“使用 redux 可以轻松撤消/重做”是半真半假的(充其量)。我发现自己的情况与您相同,需要将服务器状态与 DB/REST API 同步。奇怪的是,redux 文档或相关的 undo/redo 库都没有提到这个极其常见的用例。我敢说大多数设置都必须处理这个问题。我也在寻找一种方法来轻松撤消/重做对我的商店的更改并轻松更新我的服务器。我目前的想法是跟踪动作和动作参数,而不是状态变化。这在概念上似乎与您的方法有点相似。
标签: javascript reactjs redux undo-redo