Skip to main content
Version: 3.x

Setting callback dependencies

The callback we pass to useHotkeys gets memoised automatically inside the useHotkeys hook to prevent unnecessary re-renders. This can lead to problems with stale state. To prevent those problems, the hook accepts a dependencies array just like Reacts internal useEffect, useMemo and useCallback hooks.

The general rule is to put every unstable reference used inside the callback into the dependency array.

Let's change the second example up a bit to illustrate the problem of stale state. Just as a reminder, we build a simple counter component:

function ExampleComponent() {
const [count, setCount] = useState(0)
const ref = useHotkeys('b', () => setCount(prevCount => prevCount + 1))

return (
<span ref={ref} tabIndex={-1}>Pressed the 'b' key {count} times.</span>
)
}
Notice

Remember, that we are using the ref to scope our hotkey to this specific component. To let the component receive focus we add the tabIndex={-1} prop, since <span> tags cannot receive focus by default.


Now, what if we change up the callback execution just a bit?

function ExampleComponent() {
const [count, setCount] = useState(0)
const ref = useHotkeys('b', () => setCount(count + 1))

return (
<span ref={ref} tabIndex={-1}>Pressed the 'b' key {count} times.</span>
)
}

While the first component works as expected, the second does not and stops increasing count once we pressed the 'b' key. This is called stale state. We are referencing a variable that changes over time. Since our callback gets memoised inside useHotkeys hook, the variable count inside the callback will always hold its initial value of 0.

To solve this problem, we could go back to the previous example. But that solution is specific to the useState hook. What if we want reference something other than a state variable, like a useMemo result? We can use the dependency array for that!

function ExampleComponent() {
const [count, setCount] = useState(0)
const squaredCount = useMemo(() => count * 2, [count])

const ref = useHotkeys('b', () => setCount(squaredCount + 1), [squaredCount])

return (
<span ref={ref} tabIndex={-1}>Pressed the 'b' key {squaredCount} times.</span>
)
}

If we remove the dependency array from useHotkeys you will notice that again the callback execution seems to stop after the first key press. But as we learned this is all due to stale state. The callback gets executed properly, just the referenced value is an old one.