We are now going to create a slightly more advanced Hook for debounced undo functionality. We already implemented this functionality in the CreatePost component. Now, we are going to extract this functionality into a custom useDebouncedUndo Hook.
Let's create the useDebouncedUndo Hook with the following steps:
- Create a new src/hooks/useDebouncedUndo.js file.
- Import the useState, useEffect, and useCallback Hooks from React, as well as the useUndo Hook and the useDebouncedCallback Hook:
import { useState, useEffect, useCallback } from 'react'
import useUndo from 'use-undo'
import { useDebouncedCallback } from 'use-debounce'
- Now we are going to define the useDebouncedUndo function, which accepts a timeout argument for the debounced callback:
export default function useDebouncedUndo (timeout = 200) {
- In this function, we copy over the useState Hook from the previous implementation, as shown here:
const [ content, setInput ] = useState('')
- Next, we copy over the useUndo Hook; however, this time, we store all other undo-related functions in an undoRest object:
const [ undoContent, { set: setContent, ...undoRest } ] = useUndo('')
- Then we copy over the useDebouncedCallback Hook, replacing the fixed 200 value with our timeout argument:
const [ setDebounce, cancelDebounce ] = useDebouncedCallback(
(value) => {
setContent(value)
},
timeout
)
- Now we copy over the Effect Hook, as shown in the following code:
useEffect(() => {
cancelDebounce()
setInput(undoContent.present)
}, [cancelDebounce, undoContent])
- Then, we define a setter function, which is going to set a new input value and call setDebounce. We can wrap the setter function with a useCallback Hook here to return a memoized version of the function and avoid recreating the function every time the component that uses the Hook re-renders. Similar to the useEffect and useMemo Hooks, we also pass a dependency array as the second argument of the useCallback Hook:
const setter = useCallback(function setterFn (value) {
setInput(value)
setDebounce(value)
}, [ setInput, setDebounce ])
- Finally, we return the content variable (containing the current input value), the setter function, and the undoRest object (which contains the undo/redo functions and the canUndo/canRedo booleans):
return [ content, setter, undoRest ]
}
Creating a custom Hook for debounced undo means that we can reuse that functionality across multiple components. We could even provide this Hook as a public library, allowing others to easily implement debounced undo/redo functionality.