Skip to content

Instantly share code, notes, and snippets.

@DaBs
Last active September 22, 2021 08:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DaBs/681d54b99e460efaaf36a247ca0fb7a6 to your computer and use it in GitHub Desktop.
Save DaBs/681d54b99e460efaaf36a247ca0fb7a6 to your computer and use it in GitHub Desktop.
import { MutableRefObject, useCallback, useRef } from 'react';
export type Callback<T extends any[]> = (...args: T) => void;
export interface ICallbackTimingOptions<T extends any[]> {
singleCallback?: Callback<T>;
}
export function useCallbackTimingSequence<T extends any[]>(
callback: Callback<T>,
sequence: number,
thresholdMs: number = 300,
options: ICallbackTimingOptions<T> = {},
) {
const lastPress: MutableRefObject<number | null> = useRef(null);
const sequenceCounter = useRef(0);
const handler = useCallback(
(...args: T) => {
const now = Date.now();
if (lastPress.current && (now - lastPress.current) < thresholdMs) {
// If there was a last press, and the difference between them is less than threshold, the press is part of the sequence
sequenceCounter.current += 1;
if (sequenceCounter.current >= sequence) {
// If we're above the sequence threshold, trigger the callback
sequenceCounter.current = 0;
lastPress.current = null;
callback(...args);
} else {
// Else just set lastPress to now, to continue the sequence
lastPress.current = now;
}
} else {
// This press was outside of the threshold, so we set the counter to 1 and restart our sequencing. Also call optional callback in case
sequenceCounter.current = 1;
lastPress.current = now;
if (options.singleCallback) {
options.singleCallback(...args);
}
}
},
[callback, thresholdMs, options.singleCallback],
);
return handler;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment