Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redo #3

Merged
merged 3 commits into from
Apr 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# 🍜 Zundo

undo middleware for [zustand](https://github.com/pmndrs/zustand). built with zustand.
enable time-travel in your apps. undo/redo middleware for [zustand](https://github.com/pmndrs/zustand). built with zustand.

[![Build Size](https://img.shields.io/bundlephobia/min/zundo?label=bundle%20size&style=flat&colorA=000000&colorB=000000)](https://bundlephobia.com/result?p=zundo)
[![Version](https://img.shields.io/npm/v/zundo?style=flat&colorA=000000&colorB=000000)](https://www.npmjs.com/package/zundo)
Expand Down Expand Up @@ -38,7 +38,7 @@ Use your store anywhere and get undo from `zundo` and add it to a button to go b

```tsx
const App = () => {
const { undo } = useUndo();
const { undo, redo } = useUndo();
const { bears, increasePopulation, removeAllBears } = useStore();

return (
Expand All @@ -47,15 +47,43 @@ const App = () => {
<button onClick={increasePopulation}>increase</button>
<button onClick={removeAllBears}>remove</button>
<button onClick={undo}>undo</button>
<button onClick={redo}>redo</button>
</>
);
};
```

## API

### `undo()`

Middleware for Zustand to add the ability to time travel through states.

```tsx
import create from 'zustand';
import { undo } from 'zundo';

const useStore = create(
undo(() => ({ ... }))
);
```

### `useUndo()`

Hook that provides reference to a store containing actions that undo/redo states for your main store when called.

```tsx
const { undo, redo } = useUndo();
```

- `undo`: call function to apply previous state (if there are previous states)
- `redo`: call function to apply future state (if there are future states). Future states are "previous previous states."

Dispatching a new state will clear all of the future states.

## Road Map

- add redo. probably with index to traverse through prevStates
- possibly use better data structure for storing prevous states. Maybe just a diff between states?
- possibly use better data structure for storing previous states. Maybe just a diff between states?
- clean up api? return `undo` with the store hook?

## Contributing
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "zundo",
"version": "0.1.3",
"private": false,
"description": "🍜 Undo middleware for zustand",
"description": "🍜 undo/redo middleware for zustand",
"keywords": [
"undo",
"middleware",
Expand Down
28 changes: 24 additions & 4 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,35 @@ import create from 'zustand';

interface UndoStoreState extends State {
prevStates: any[];
futureStates: any[];
undo: () => void;
handle: Function;
redo: () => void;
setStore: Function;
getStore: Function;
}

// Stores previous actions
const undoStore = createVanilla<UndoStoreState>((_, get) => ({
prevStates: [],
undo: () => {
get().handle(get().prevStates.pop());
const prevStates = get().prevStates;
if (prevStates.length > 0) {
const prevState = prevStates.pop();
get().futureStates.push(get().getStore());
get().setStore(prevState);
}
},
setStore: () => {},
getStore: () => {},
futureStates: [],
redo: () => {
const futureStates = get().futureStates;
if (futureStates.length > 0) {
const futureState = futureStates.pop();
get().prevStates.push(get().getStore());
get().setStore(futureState);
}
},
handle: () => {},
}));
const { getState, setState } = undoStore;
export const useUndo = create(undoStore);
Expand All @@ -36,7 +54,9 @@ export const undo = <TState extends State>(config: StateCreator<TState>) => (
args => {
setState({
prevStates: [...getState().prevStates, { ...get() }],
handle: set,
setStore: set,
futureStates: [],
getStore: get,
});
set(args);
},
Expand Down
10 changes: 8 additions & 2 deletions stories/bears.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,21 +35,27 @@ const useStore = create<StoreState>(
);

const App = () => {
const { prevStates, undo } = useUndo();
const { bears, increasePopulation, removeAllBears } = useStore();
const { prevStates, undo, futureStates, redo } = useUndo();
const store = useStore();
const { bears, increasePopulation, removeAllBears } = store;

return (
<div>
<h1>🐻 ♻️ Zundo!</h1>
previous states: {JSON.stringify(prevStates)}
<br />
future states: {JSON.stringify(futureStates)}
<br />
current state: {JSON.stringify(store)}
<br />
<br />
bears: {bears}
<br />
<button onClick={increasePopulation}>increase</button>
<button onClick={removeAllBears}>remove</button>
<br />
<button onClick={undo}>undo</button>
<button onClick={redo}>redo</button>
</div>
);
};
Expand Down