(state, action) => state
signature makes it natural to implement generic “reducer enhancers” or “higher order reducers”. They are functions that take your reducer and enhance it with some additional functionality while preserving its signature. Undo history is exactly such a case.UNDO
and REDO
. In our reducer, we will do the following steps to handle these actions:past
.present
to the element we removed in the previous step.present
state at the beginning of the future
.future
.present
to the element we removed in the previous step.present
state at the end of the past
.present
at the end of the past
.present
to the new state after handling the action.future
.present
state from? We don’t seem to know it beforehand.present
to the past
?present
state to a custom reducer?combineReducers()
is also a reducer enhancer because it takes reducers and returns a new reducer.undoable
should have been:undoable
reducer enhancer to teach it to react to UNDO
and REDO
actions..present
to the current state when you retrieve it. You may also check .past.length
and .future.length
to determine whether to enable or to disable the Undo and Redo buttons, respectively.undoable
ourselves? Sure, we can! Meet Redux Undo, a library that provides simple Undo and Redo functionality for any part of your Redux tree.todos-with-undo
example that comes with Redux.undoable
reducer enhancer.undoable
function. For example, if you exported a todos
reducer from a dedicated file, you will want to change it to export the result of calling undoable()
with the reducer you wrote:reducers/todos.js
distinctState()
filter serves to ignore the actions that didn’t result in a state change. There are many other options to configure your undoable reducer, like setting the action type for Undo and Redo actions.combineReducers()
call will stay exactly as it was, but the todos
reducer will now refer to the reducer enhanced with Redux Undo:reducers/index.js
undoable
at any level of the reducer composition hierarchy. We choose to wrap todos
instead of the top-level combined reducer so that changes to visibilityFilter
are not reflected in the undo history.todos
part of the state looks like this:state.todos.present
instead of just state.todos
:containers/VisibleTodoList.js
UndoRedo
for these buttons. We won’t bother to split the presentational part into a separate file because it is very small:containers/UndoRedo.js
connect()
from React Redux to generate a container component. To determine whether to enable Undo and Redo buttons, you can check state.todos.past.length
and state.todos.future.length
. You won’t need to write action creators for performing undo and redo because Redux Undo already provides them:containers/UndoRedo.js
UndoRedo
component to the App
component:components/App.js