最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - What is the best way to implement undo state change (undo storehistory implementation) in React Redux - Stack Overf

programmeradmin1浏览0评论

I'm looking out for a history redo/ store reset to the previous state in a redux react application.

I've found a blog telling it can be done by storing present, future and past states in a stack and resetting accordingly.

I've also found a similar question in StackOverflow, but it doesn't give me a proper answer or maybe it's difficult for me to understand.

I've built a demo ToDo app and have used redux-logger to log store details with previous state and updated state. You can find the code here.

Do we have a store reset method in redux, so that we can get the previous state and update the store other that having a store with the present, the past and future states?

I'm looking out for a history redo/ store reset to the previous state in a redux react application.

I've found a blog telling it can be done by storing present, future and past states in a stack and resetting accordingly.

I've also found a similar question in StackOverflow, but it doesn't give me a proper answer or maybe it's difficult for me to understand.

I've built a demo ToDo app and have used redux-logger to log store details with previous state and updated state. You can find the code here.

Do we have a store reset method in redux, so that we can get the previous state and update the store other that having a store with the present, the past and future states?

Share Improve this question edited May 23, 2017 at 12:02 CommunityBot 11 silver badge asked Mar 28, 2017 at 5:03 sudheeshcmsudheeshcm 3,4184 gold badges15 silver badges22 bronze badges
Add a comment  | 

3 Answers 3

Reset to default 11

For anyone looking for a solution to this in 2020. You don't have to store the entire state object as the present, past, and future.

Instead, you can just store details about what has changed. This can be implemented using ImmerJS. It records all changes done on the state object and generates something called a patch.

Example: If age is updated from 32 to 40, then the generated patch will be:

Patch: [ { op: 'replace', path: [ 'age' ], value: 40 } ]

Inverse Patch: [ { op: 'replace', path: [ 'age' ], value: 32 } ]

It also exposes a method to apply these patches/inverse patches to the state - applyPatch. So, to undo we can apply an inverse patch and to redo we can apply a patch.

You can find details of full implementation here: Implementing Undo-Redo Functionality in Redux using Immer

What is the best way ...

Best way is always difficult to define, it really depends of your use-case and requirements included client and server.

But to get start you you could consider using a library or looking how they approach this problem, some example:

https://github.com/omniscientjs/immstruct

https://www.npmjs.com/package/redux-undo

https://github.com/PowToon/redux-undo-redo

Tutorial with example of todo undo/redo in redux: https://github.com/reactjs/redux/tree/master/examples/todos-with-undo

Or you could implement your own, as redux you could store all your application state. A simple stack could be a simple and efficient way to store your app state at any given time.

let yourHistory = [state1, state2, state3];

I created a state undo/redo snapshot manager class, which would be great for tracking the change history on an HTML element.

  <div id="buttons">
     <button type="button" id="undo_btn">Undo</button>
     <button type="button" id="redo_btn">Redo</button>
  </div>
  <br/><br/>
  <div id="content">
     <label>
        Input1:
        <input type="text" value="" />
     </label>
     <br/><br/>
     <label>
        Input2:
        <input type="text" value="" />
     </label>
     <br/><br/>
     <label>
        Input3:
        <input type="text" value="" />
     </label>
     <br/><br/>
     <label>
        Input4:
        <input type="text" value="" />
     </label>
     <br/><br/>
  </div>

  <script type="text/javascript">
  var StateUndoRedo = function() {
     var init = function(opts) {
        var self = this;
        self.opts = opts;
        if(typeof(self.opts['undo_disabled']) == 'undefined') {
           self.opts['undo_disabled'] = function() {};
        }
        if(typeof(self.opts['undo_enabled']) == 'undefined') {
           self.opts['undo_enabled'] = function() {};
        }
        if(typeof(self.opts['redo_disabled']) == 'undefined') {
           self.opts['redo_disabled'] = function() {};
        }
        if(typeof(self.opts['redo_enabled']) == 'undefined') {
           self.opts['redo_enabled'] = function() {};
        }
        if(typeof(self.opts['restore']) == 'undefined') {
           self.opts['restore'] = function() {};
        }
        self.opts['undo_disabled']();
        self.opts['redo_disabled']();
     }

     var add = function(state) {
        var self = this;
        if(typeof(self.states) == 'undefined') {
           self.states = [];
        }
        if(typeof(self.state_index) == 'undefined') {
           self.state_index = -1;
        }
        self.state_index++;
        self.states[self.state_index] = state;
        self.states.length = self.state_index + 1;
        if(self.state_index > 0) {
           self.opts['undo_enabled']();
        }
        self.opts['redo_disabled']();
     }

     var undo = function() {
        var self = this;
        if(self.state_index > 0) {
           self.state_index--;
           if(self.state_index == 0) {
              self.opts['undo_disabled']();
           } else {
              self.opts['undo_enabled']();
           }
           self.opts['redo_enabled']();

           self.opts['restore'](self.states[self.state_index]);
       }
     }

     var redo = function() {
        var self = this;
        if(self.state_index < self.states.length) {
           self.state_index++;
           if(self.state_index == self.states.length - 1) {
              self.opts['redo_disabled']();
           } else {
              self.opts['redo_enabled']();
           }
           self.opts['undo_enabled']();

           self.opts['restore'](self.states[self.state_index]);
       }
     }

     var restore = function() {
        var self = this;
        self.opts['restore'](self.states[self.state_index]);
     }

     var clear = function() {
        var self = this;
        self.state_index = 0;
        //self.states = [];
     }

     return {
        init: init,
        add: add,
        undo: undo,
        redo: redo,
        restore: restore,
        clear: clear
     };
  };

  //initialize object
  var o = new StateUndoRedo();
  o.init({
     'undo_disabled': function() {
        //make the undo button hidden
        document.getElementById("undo_btn").disabled = true;
     },
     'undo_enabled': function() {
        //make the undo button visible
        document.getElementById("undo_btn").disabled = false;
     },
     'redo_disabled': function() {
        //make the redo button hidden
        document.getElementById("redo_btn").disabled = true;
     },
     'redo_enabled': function() {
        //make the redo button visible
        document.getElementById("redo_btn").disabled = false;
     },
     'restore': function(state) {
        //replace the current content with the restored state content
        document.getElementById("content").innerHTML = state;
     }
  });

  //initialize first state
  o.add(document.getElementById("content").innerHTML);
  o.restore();
  o.clear();

  //bind click events for undo/redo buttons
  document.getElementById("undo_btn").addEventListener("click", function() {
     o.undo();
  });
  document.getElementById("redo_btn").addEventListener("click", function() {
     o.redo();
  });

  //bind change events for content element
  document.getElementById('content').addEventListener("change", function(event) {
     // the following is required since vanilla JS innerHTML 
     // does not capture user-changed values of inputs
     // so we set the attributes explicitly (use jQuery to avoid this)
     var elems = document.querySelectorAll("#content input");
     for(var i = 0; i < elems.length; i++) {
        elems[i].setAttribute("value", elems[i].value);
     }

     //take a snapshot of the current state of the content element
     o.add(document.getElementById("content").innerHTML);
  });
  </script>

See this JSFiddle: https://jsfiddle.net/up73q4t0/56/

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论