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 badges3 Answers
Reset to default 11For 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/