Redux 是不是有内置的方法来撤消操作?

Posted

技术标签:

【中文标题】Redux 是不是有内置的方法来撤消操作?【英文标题】:Does Redux have a built-in way to undo actions?Redux 是否有内置的方法来撤消操作? 【发布时间】:2015-12-08 04:14:44 【问题描述】:

我正在构建一个在用户向下滚动时执行操作的应用。如果我可以在用户再次向上滚动时撤消这些操作,基本上将滚动变成一种浏览操作时间线的方式。

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

【问题讨论】:

【参考方案1】:

Redux 中是否有内置方法可以做到这一点?还是我必须为此编写中间件?

在这种情况下,中间件听起来像是错误的想法,因为这纯粹是状态管理问题。相反,您可以编写一个函数,该函数接受一个 reducer 并返回一个 reducer,并通过沿途的动作历史跟踪来“增强”它。

我在this answer 中概述了这种方法,它类似于redux-undo 的工作方式,不同之处在于您可以存储动作而不是存储状态。 (取决于您想要做出的权衡,以及能够以与发生顺序不同的顺序“取消”操作是否重要。)

【讨论】:

这正是我最终所做的——尽管我确实存储了状态。尽管以不同的顺序取消操作并不重要,但在记忆方面,存储操作会更好。但是,我不知道我将如何从减速器中再次将它们发射出去 - 你知道那是怎么做到的吗? @Vincent 你不需要再次触发它们——只需通过运行state.actions.reduce(reducer) 之类的方法重新计算present 哈,有道理,谢谢。另外,我刚刚注意到你是先生。还原;非常感谢这个优秀的图书馆!【参考方案2】:

我相信这个想法与其说是“撤消”,不如说是在每次操作通过 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

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

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

【讨论】:

对。我现在在想我将不得不编写一个响应滚动操作以保存当前状态的减速器 - 唯一的问题是我还不知道如何让该减速器都读取我想要的状态树部分保存,然后将其复制到状态树的不同部分。请注意,这不仅仅是为了开发;我的用例是用于实际的用户交互。 如果减速器需要访问状态树的两个不同部分,这可能意味着您需要一个将两者组合在一起的减速器 @Vincent 或者,第二种状态实际上不是基本的(即不是事实的来源),可以丢弃 嗯,最后一句话是有道理的——如果我保留状态的历史,我也应该能够只保留指向当前版本的指针(或者只使用最新添加的堆)。谢谢! 我应该补充一点,我同意了这个,并在vincenttunru.com/Composing-Redux-reducers 上更详细地描述了我是如何实现这个的【参考方案3】:

我还想创建一个简单的撤消功能,但已经发布了一个带有redux-storage 的应用程序,它可以为每个用户序列化和加载状态。所以为了保持它向后兼容,我不能使用任何包装我的状态键的解决方案,就像redux-undo 对past: []present: 所做的那样。

寻找替代方案,Dan's tutorial 启发我重写 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
);

对我来说,这就像一个魅力,只是看起来不太漂亮。如果这是一个好/坏的主意以及原因,我会很高兴收到任何反馈;-)

【讨论】:

【参考方案4】:

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

【讨论】:

对,所以我要自己写中间件了?

以上是关于Redux 是不是有内置的方法来撤消操作?的主要内容,如果未能解决你的问题,请参考以下文章

gitGit 基础 - 撤消操作

撤消文件 readline() 操作,使文件指针回到原始状态

Git 撤消操作

Git Book 中文版 - Git的撤消操作 - 重置, 签出 和 撤消

撤消对 Clojure 中内置函数的覆盖

React-Redux 设计方法 - 可编辑文本字段双向绑定的 Redux 操作或本地函数