·
1 min read
·1.5K views
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:
stores/counter.ts
import { ref } from 'vue';
import { defineStore } from 'pinia';
export const useCounterStore = defineStore('counter', () => {
  const count = ref(0);
  function increment() {
    count.value++;
  }
  function decrement() {
    count.value--;
  }
  return { count, increment, decrement };
});
Now let's use that store in a Vue component:
components/Counter.vue
<script setup lang="ts">
const counterStore = useCounterStore();
const { count } = storeToRefs(counterStore);
</script>
<template>
  <div class="container">
    <span>count is {{ counterStore.count }}</span>
    <button @click="counterStore.increment">Increment</button>
    <button @click="counterStore.decrement">Decrement</button>
  </div>
</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:
components/Counter.vue
<script setup lang="ts">
const counterStore = useCounterStore();
const { count } = storeToRefs(counterStore);
const { history, undo, redo } = useRefHistory(count, {
  capacity: 50, // limit history records
});
</script>
<template>
  <div class="container">
    <span>count is {{ counterStore.count }}</span>
    <button @click="counterStore.increment">Increment</button>
    <button @click="counterStore.decrement">Decrement</button>
  </div>
  <div class="container" style="margin-top: 20px">
    <span>History</span>
    <button @click="undo">Undo</button>
    <button @click="redo">Redo</button>
    <ul>
      <li v-for="entry of history" :key="entry.timestamp">
        <span>{{ entry }}</span>
      </li>
    </ul>
  </div>
</template>
Demo
Try it yourself in the following StackBlitz:
If you liked this Vue tip, follow me on BlueSky to get notified about new tips, blog posts, and more. Alternatively (or additionally), you can subscribe to my weekly Vue & Nuxt newsletter :



