I recently needed to implement a simple history with undo and redo functionality for a project I'm working on. I decided to use the useRefHistory composable from VueUse to implement this functionality.
Defining the store
Let's first take a look at the store we use for demonstration purposes which is a simple counter store:
1import { ref } from 'vue';
2import { defineStore } from 'pinia';
3
4export const useCounterStore = defineStore('counter', () => {
5 const count = ref(0);
6
7 function increment() {
8 count.value++;
9 }
10
11 function decrement() {
12 count.value--;
13 }
14
15 return { count, increment, decrement };
16});
Now let's use that store in a Vue component:
1<script setup lang="ts">
2const counterStore = useCounterStore();
3
4const { count } = storeToRefs(counterStore);
5</script>
6
7<template>
8 <div class="container">
9 <span>count is {{ counterStore.count }}</span>
10 <button @click="counterStore.increment">Increment</button>
11 <button @click="counterStore.decrement">Decrement</button>
12 </div>
13</template>
Implementing the history
Now that we have a store, let's implement the history functionality. We can do this by using the useRefHistory
composable from VueUse:
1<script setup lang="ts">
2const counterStore = useCounterStore();
3
4const { count } = storeToRefs(counterStore);
5
6const { history, undo, redo } = useRefHistory(count, {
7 capacity: 50, // limit history records
8});
9</script>
10
11<template>
12 <div class="container">
13 <span>count is {{ counterStore.count }}</span>
14 <button @click="counterStore.increment">Increment</button>
15 <button @click="counterStore.decrement">Decrement</button>
16 </div>
17
18 <div class="container" style="margin-top: 20px">
19 <span>History</span>
20 <button @click="undo">Undo</button>
21 <button @click="redo">Redo</button>
22
23 <ul>
24 <li v-for="entry of history" :key="entry.timestamp">
25 <span>{{ entry }}</span>
26 </li>
27 </ul>
28 </div>
29</template>
Demo
Try it yourself in the following StackBlitz:
If you liked this Vue tip, follow me on X to get notified about new tips, blog posts, and more. Alternatively (or additionally), you can subscribe to my weekly Vue & Nuxt newsletter:
Vue Tip: Accessing Template Ref in Child Component
Learn how to access template refs in a child component within a Vue.js application, enabling you to manipulate and interact with specific elements in the child component's template from the parent component.
Vue Tip: Automatically Import Components
Use the unplugin-vue-components tool to automatically import your Vue components.