问题 Redux是否有内置的撤消操作的方法?


我正在构建一个应用程序,在用户向下滚动时执行操作。如果我可以在用户再次向上滚动时撤消这些操作,那将是很好的,基本上将滚动转换为浏览动作时间线的方式。

Redux中是否有内置方式来执行此操作?或者我是否必须为此编写中间件?


3897
2017-09-11 14:46


起源



答案:


我相信这个想法并没有那么多“撤销”,就像每次动作通过redux时保存对整个状态树的引用一样多。

您将在不同时间拥有由应用程序状态组成的历史堆栈。

let history = [state1, state2, state3]

// some action happens

let history = [state1, state2, state3, state4]

// some action happens

let history = [state1, state2, state3, state4, state5]

// undo an action

let history = [state1, state2, state3, state4]

state = state4

要“撤消”某个操作,只需将应用程序状态替换为其中一个已保存的状态即可。

这可以通过支持结构共享的数据结构来提高效率,但在开发过程中,我们并不需要过多地考虑资源限制。


3
2017-09-13 04:42



对。我现在想我必须编写一个reducer来响应滚动操作以保存当前状态 - 唯一的事情是我还不知道如何让reducer读取我想要的状态树的一部分保存,然后将其复制到 不同 状态树的一部分。请注意,这不仅仅用于开发;我的用例是用于实际的用户交互。 - Vincent
如果reducer需要访问状态树的两个不同部分,这可能意味着你需要一个将它们组合在一起的reducer - Qiming
@Vincent或者,第二个状态实际上不是基本的(即不是真理的来源),可以被丢弃 - Qiming
嗯,最后一句话是有道理的 - 如果我保持状态的历史,我也应该能够保持指向当前版本的指针(或者只是使用最新的堆栈添加)。谢谢! - Vincent
我应该补充说,我接受了这个并描述了我如何更详细地实现这一点 vincenttunru.com/Composing-Redux-reducers - Vincent


答案:


我相信这个想法并没有那么多“撤销”,就像每次动作通过redux时保存对整个状态树的引用一样多。

您将在不同时间拥有由应用程序状态组成的历史堆栈。

let history = [state1, state2, state3]

// some action happens

let history = [state1, state2, state3, state4]

// some action happens

let history = [state1, state2, state3, state4, state5]

// undo an action

let history = [state1, state2, state3, state4]

state = state4

要“撤消”某个操作,只需将应用程序状态替换为其中一个已保存的状态即可。

这可以通过支持结构共享的数据结构来提高效率,但在开发过程中,我们并不需要过多地考虑资源限制。


3
2017-09-13 04:42



对。我现在想我必须编写一个reducer来响应滚动操作以保存当前状态 - 唯一的事情是我还不知道如何让reducer读取我想要的状态树的一部分保存,然后将其复制到 不同 状态树的一部分。请注意,这不仅仅用于开发;我的用例是用于实际的用户交互。 - Vincent
如果reducer需要访问状态树的两个不同部分,这可能意味着你需要一个将它们组合在一起的reducer - Qiming
@Vincent或者,第二个状态实际上不是基本的(即不是真理的来源),可以被丢弃 - Qiming
嗯,最后一句话是有道理的 - 如果我保持状态的历史,我也应该能够保持指向当前版本的指针(或者只是使用最新的堆栈添加)。谢谢! - Vincent
我应该补充说,我接受了这个并描述了我如何更详细地实现这一点 vincenttunru.com/Composing-Redux-reducers - Vincent


Redux中是否有内置方式来执行此操作?或者我是否必须为此编写中间件?

在这种情况下,中间件听起来像是错误的想法,因为这纯粹是对国家管理的关注。相反,您可以编写一个带有reducer并返回reducer的函数,并在此过程中使用操作历史跟踪“增强”它。

我概述了这种方法 这个答案,它和如何相似 终极版,撤消 除了不存储状态外,您可以存储操作。 (取决于你想要做出的权衡,以及是否能够以不同于他们发生的顺序“取消”行动是很重要的。)


9
2017-10-03 12:48



这正是我最终做的事情 - 虽然我确实存储了州。尽管以不同的顺序取消操作并不重要,但在内存方面,存储操作的效果会更好。但是,我不知道如何再从减速机上取下它们 - 你知道怎么做到的吗? - Vincent
@Vincent你不需要再次解雇他们 - 只需重新计算 present 通过运行类似的东西 state.actions.reduce(reducer)。 - Dan Abramov
哈,有道理,谢谢。而且,我只是注意到你是先生。终极版;非常感谢这个优秀的图书馆! - Vincent


我还想创建一个简单的撤消功能,但已经发布了一个应用程序 终极版的存储 为每个用户序列化并加载状态。因此,为了保持向后兼容,我无法使用包装我的状态键的任何解决方案,例如 终极版,撤消 做的 past: [] 和 present:

寻找替代方案, 丹的教程 激励我超越 combineReducers。现在我有一部分州: history 这可以节省多达10份其他州的副本并将其弹出 UNDO 行动。这是代码,这可能也适用于您的情况:

function shouldSaveUndo(action){
  const blacklist = ['@@INIT', 'REDUX_STORAGE_SAVE', 'REDUX_STORAGE_LOAD', 'UNDO'];

  return !blacklist.includes(action.type);
}

function combineReducers(reducers){
  return (state = {}, action) => {
    if (action.type == "UNDO" && state.history.length > 0){
      // Load previous state and pop the history
      return {
        ...Object.keys(reducers).reduce((stateKeys, key) => {
          stateKeys[key] = state.history[0][key];
          return stateKeys;
        }, {}),
        history: state.history.slice(1)
      }
    } else {
      // Save a new undo unless the action is blacklisted
      const newHistory = shouldSaveUndo(action) ?
        [{
          ...Object.keys(reducers).reduce((stateKeys, key) => {
            stateKeys[key] = state[key];
            return stateKeys;
          }, {})
        }] : undefined;

      return {
        // Calculate the next state
        ...Object.keys(reducers).reduce((stateKeys, key) => {
          stateKeys[key] = reducers[key](state[key], action);
          return stateKeys;
        }, {}),
        history: [
          ...(newHistory || []),
          ...(state.history || [])
        ].slice(0, 10)
      };
    }
  };
}


export default combineReducers({
  reducerOne,
  reducerTwo,
  reducerThree
});

对我来说,这就像一个魅力,它看起来不是很漂亮。如果这是一个好/坏想法以及为什么,我会很高兴得到任何反馈;-)


2
2017-12-23 15:47





没有内置的方法来做到这一点。 但你可以从redux-dev-tools的工作原理中获得灵感(https://github.com/gaearon/redux-devtools)。它基本上具有“时间旅行”功能,它通过跟踪所有操作并每次重新评估它们来工作。因此,您可以轻松浏览所有更改。


1
2017-09-11 16:05



是的,所以它归结为编写我自己的中间件? - Vincent