React Hooks are a fundamentally simpler way to encapsulate custom behavior in user interfaces.

If stateful React components are like an atom, hooks are the subatomic particles. We are given primitives like useState and useReducer, and we can compose and remix them in various quantities to create new elements with the exact properties we want.

Where useState is useful for reading and mutating atomic values, useReducer is better for updating collections (arrays and objects) of data that represent more complex states. It borrows heavily from Redux patterns in dispatching typed actions and passing through simple state machines. I will refer the reader to the docs rather than repeat them here.

My favorite of the six principles of User Interface Design is Tolerance, which asks the UI designer to limit the cost of mistakes by the user. By giving the user an implicit assurance that their work won’t be lost without intention, they encourage play and discovery of everything else in the UI, which enables more serendipity with lower cognitive load. Many apps I see don’t pass this simple test, and don’t offer undo/redo capability even in UI that doesn’t interact with a backend, probably because it is extra work.

We should make it easier to write Tolerant User Interfaces.

One easy way to reduce the number of irreversible actions is to make more things reversible. Implementing this with typical state primitives in React is troublesome, because those APIs tend to discard past state in favor of new state.

So how do we implement tolerance in our React apps? We can stick historical state in another piece of state, or a global variable, but those solutions are problematic too. Better to treat state history just like another part of state:

const timeline ={
past:[],
present: initialState,
future:[]};

Because we’re working with objects and arrays, we’d like to avoid defensive copying and use local mutability with structural sharing, so we introduce immer:

In order to accomplish this, we need to transform reducer and initialState into the timeline data structure, and then return nicely named, easy to work with variables in the result. This is trivial in custom hooks: