JAVASCRIPT
Advanced Undo/Redo for Reactive State with Vue 3 Composable
Build a robust undo/redo functionality for any reactive state in Vue 3 using a custom composable, enabling history tracking and state restoration.
// composables/useUndoRedo.js
import { ref, watch, computed } from 'vue';
export function useUndoRedo(initialValue) {
const history = ref([initialValue]);
const currentIndex = ref(0);
const state = ref(initialValue);
// Watch for changes in the state and add to history
watch(state, (newValue) => {
// If we're not at the end of history (i.e., we've undone some changes),
// truncate the history and add the new change.
if (currentIndex.value < history.value.length - 1) {
history.value = history.value.slice(0, currentIndex.value + 1);
}
history.value.push(newValue);
currentIndex.value = history.value.length - 1;
}, { deep: true });
const canUndo = computed(() => currentIndex.value > 0);
const canRedo = computed(() => currentIndex.value < history.value.length - 1);
const undo = () => {
if (canUndo.value) {
currentIndex.value--;
state.value = history.value[currentIndex.value];
}
};
const redo = () => {
if (canRedo.value) {
currentIndex.value++;
state.value = history.value[currentIndex.value];
}
};
return {
state,
undo,
redo,
canUndo,
canRedo,
};
}
// src/App.vue
<template>
<div>
<h1>Undo/Redo Example</h1>
<textarea v-model="editorState.text" rows="5" cols="50"></textarea>
<div>
<button @click="undo()" :disabled="!canUndo">Undo</button>
<button @click="redo()" :disabled="!canRedo">Redo</button>
</div>
<p>Current Text: {{ editorState.text }}</p>
</div>
</template>
<script setup>
import { useUndoRedo } from './composables/useUndoRedo';
const { state: editorState, undo, redo, canUndo, canRedo } = useUndoRedo({ text: '' });
// Example: you can also directly modify editorState.text, and it will be tracked
// editorState.value.text = 'Some new text';
</script>
How it works: This snippet presents a powerful `useUndoRedo` composable for Vue 3 that provides undo/redo capabilities for any reactive state. It maintains a `history` array of state snapshots and a `currentIndex` to track the current position. A `watch` on the `state` automatically pushes new changes to the history. `undo` and `redo` functions navigate this history, updating the `state` to previous or future versions. `canUndo` and `canRedo` computed properties provide checks for button disabling, making the composable highly reusable for various data types (objects, primitives, arrays) and user interfaces.