Reactive refs are a great way to store reactive information. However, by themselves, they can only store the current state of things. With the help from ViewUse though, we can actually keep up with the history of a reactive reference's value, and then undo or redo those values at will. This is possible with the UseRefHistory composable.
Let's say that we had a reactive ref called Name. Well, we can call UseRefHistory on it. and then destructure out the ref's history, an undo function, and a redo function. Next, inside of the template, let's bind name to a text input, and then we can print out the ref history below it.
I'll also include a break tag just to break things up, and wrap history in a pretag so that we can see it a little better. Now, let's go ahead and also provide some controls to call the undo and redo functions. I'll add an undo button that calls undo, and likewise a redo button that calls redo. Over in the app preview now, you can see that we have a single entry in the ref's history, and that represents its initial state.
When I type in the input, new entries get added along with the timestamps for when the change was made. That's pretty cool. If I were to click on the undo button a few times, sure enough, the value of our ref steps back through time. And pressing redo also works as expected.
Let's say, though, that keeping track of every single change is a little too much. I'd only like to commit the change to history after the user has paused typing for a second or so to get a little bit more of their full thought in each history entry. Well, to do that, we can change out useRefHistory or use debouncedRefHistory. Next, we'll pass the debounce option and set the number of milliseconds that we'd like to delay before considering the change worthy of committing to history.
Let's say one second. Alright, this time every single letter doesn't make it into the history. Instead, we only get a new history after I've paused for a moment, and then if I resume typing and pause again, it records a new entry again. Now, honestly, this isn't a super useful thing with form fields.
as the browser does a pretty good job handling form field states on its own. That is, I could remove the ref history altogether, and then if I type in my input, I can still use command-z and command-shift-z to undo the field's value and then redo it again. Even if I had multiple inputs together and typed something different in each of them, the browser even handles undo and redo across those different fields. Granted, this is only the full field value at a time, and not individual characters.
But this is actually a design decision of the browser that's pretty nice, since text inputs are supposed to be for short values anyways. If I change it out to text area, my browser-native undo-redo works per line. I'll type a few lines of text, command-z a few times, and then command-shift-z. Of course, my undo redo buttons no longer work as expected, but that's okay.
Most people are cool with just using the keyboard shortcuts. Where this composable might be most useful then is with interactive interfaces that encompass more than just native inputs. Let's take a simple to-do app as an example. First, I'll create a text input to enter new to-dos, and I'll also create a button to commit the to-dos.
Lastly, in the markup, we'll need a list to display the to-do items. Now, in order to change our logic, we can change the name of our reactive ref to new to-do. And I'll also create a new reactive ref called to-dos. This reactive ref will be responsible for holding our list of to-dos.
Next, we can use vModel to bind new to-do to the text input. And on click of the create to-do button, we can add the new to-do to the list of to-dos. with dot unshift. Oh, and let's also reset new todo.
Lastly, for our unordered list, we can loop over each of the todos and show each one. Awesome. Now, over on the right hand side, I'll just give the page a refresh for good measure. Then, I'll create a new todo and try hitting command Z to undo it.
However, our browser doesn't know how to handle command Z in this context of our todo out. So this is probably one of those use cases where useRefHistory will really come in handy. Let's try that out. First, I'll call useRefHistory on toDo's and deconstruct out the history, undo, and redo functions as we did before.
If I give the page a refresh now, you can see the initial state of our toDo's array from where we're printing out the history earlier. Okay, I'll add a new toDo. and I should expect to see the new entry in the history. I don't though.
Instead, the snapshot on the first history entry is updated. That's not what we wanted. Okay, to make sure that working with arrays and objects will behave as expected, we need to pass the deep option. When we use the deep option, useRefHistory will accurately track the changes of nested items and create clones of the values for each history record.
Cool. This time when I create a new to-do. Yeah, a new entry is added into history and clicking undo works as expected. Also, I could hit redo and it works as well.
Perfect. Finally, if you'd like to limit the size of the history, then you can do so with the capacity option. This means that any history older than the last 15 will be automatically forgotten. In conclusion, If you're working on a project where you need to recall the history of a particular piece of state, then the use ref history composable might be just what you're looking for.
Reactive refs are a great way to store reactive information. However, by themselves, they can only store the current state of things. With the help from ViewUse though, we can actually keep up with the history of a reactive reference's value, and then undo or redo those values at will. This is possible with the UseRefHistory composable.
Let's say that we had a reactive ref called Name. Well, we can call UseRefHistory on it. and then destructure out the ref's history, an undo function, and a redo function. Next, inside of the template, let's bind name to a text input, and then we can print out the ref history below it.
I'll also include a break tag just to break things up, and wrap history in a pretag so that we can see it a little better. Now, let's go ahead and also provide some controls to call the undo and redo functions. I'll add an undo button that calls undo, and likewise a redo button that calls redo. Over in the app preview now, you can see that we have a single entry in the ref's history, and that represents its initial state.
When I type in the input, new entries get added along with the timestamps for when the change was made. That's pretty cool. If I were to click on the undo button a few times, sure enough, the value of our ref steps back through time. And pressing redo also works as expected.
Let's say, though, that keeping track of every single change is a little too much. I'd only like to commit the change to history after the user has paused typing for a second or so to get a little bit more of their full thought in each history entry. Well, to do that, we can change out useRefHistory or use debouncedRefHistory. Next, we'll pass the debounce option and set the number of milliseconds that we'd like to delay before considering the change worthy of committing to history.
Let's say one second. Alright, this time every single letter doesn't make it into the history. Instead, we only get a new history after I've paused for a moment, and then if I resume typing and pause again, it records a new entry again. Now, honestly, this isn't a super useful thing with form fields.
as the browser does a pretty good job handling form field states on its own. That is, I could remove the ref history altogether, and then if I type in my input, I can still use command-z and command-shift-z to undo the field's value and then redo it again. Even if I had multiple inputs together and typed something different in each of them, the browser even handles undo and redo across those different fields. Granted, this is only the full field value at a time, and not individual characters.
But this is actually a design decision of the browser that's pretty nice, since text inputs are supposed to be for short values anyways. If I change it out to text area, my browser-native undo-redo works per line. I'll type a few lines of text, command-z a few times, and then command-shift-z. Of course, my undo redo buttons no longer work as expected, but that's okay.
Most people are cool with just using the keyboard shortcuts. Where this composable might be most useful then is with interactive interfaces that encompass more than just native inputs. Let's take a simple to-do app as an example. First, I'll create a text input to enter new to-dos, and I'll also create a button to commit the to-dos.
Lastly, in the markup, we'll need a list to display the to-do items. Now, in order to change our logic, we can change the name of our reactive ref to new to-do. And I'll also create a new reactive ref called to-dos. This reactive ref will be responsible for holding our list of to-dos.
Next, we can use vModel to bind new to-do to the text input. And on click of the create to-do button, we can add the new to-do to the list of to-dos. with dot unshift. Oh, and let's also reset new todo.
Lastly, for our unordered list, we can loop over each of the todos and show each one. Awesome. Now, over on the right hand side, I'll just give the page a refresh for good measure. Then, I'll create a new todo and try hitting command Z to undo it.
However, our browser doesn't know how to handle command Z in this context of our todo out. So this is probably one of those use cases where useRefHistory will really come in handy. Let's try that out. First, I'll call useRefHistory on toDo's and deconstruct out the history, undo, and redo functions as we did before.
If I give the page a refresh now, you can see the initial state of our toDo's array from where we're printing out the history earlier. Okay, I'll add a new toDo. and I should expect to see the new entry in the history. I don't though.
Instead, the snapshot on the first history entry is updated. That's not what we wanted. Okay, to make sure that working with arrays and objects will behave as expected, we need to pass the deep option. When we use the deep option, useRefHistory will accurately track the changes of nested items and create clones of the values for each history record.
Cool. This time when I create a new to-do. Yeah, a new entry is added into history and clicking undo works as expected. Also, I could hit redo and it works as well.
Perfect. Finally, if you'd like to limit the size of the history, then you can do so with the capacity option. This means that any history older than the last 15 will be automatically forgotten. In conclusion, If you're working on a project where you need to recall the history of a particular piece of state, then the use ref history composable might be just what you're looking for.