perf: Improve performance of rendering context menus
This commit is contained in:
@@ -1,28 +0,0 @@
|
||||
import * as React from "react";
|
||||
|
||||
export default function useDebouncedCallback<T>(
|
||||
callback: (...params: T[]) => unknown,
|
||||
wait: number
|
||||
) {
|
||||
// track args & timeout handle between calls
|
||||
const argsRef = React.useRef<T[]>();
|
||||
const timeout = React.useRef<ReturnType<typeof setTimeout>>();
|
||||
|
||||
function cleanup() {
|
||||
if (timeout.current) {
|
||||
clearTimeout(timeout.current);
|
||||
}
|
||||
}
|
||||
|
||||
// make sure our timeout gets cleared if consuming component gets unmounted
|
||||
React.useEffect(() => cleanup, []);
|
||||
return function (...args: T[]) {
|
||||
argsRef.current = args;
|
||||
cleanup();
|
||||
timeout.current = setTimeout(() => {
|
||||
if (argsRef.current) {
|
||||
callback(...argsRef.current);
|
||||
}
|
||||
}, wait);
|
||||
};
|
||||
}
|
||||
40
app/hooks/useThrottledCallback.ts
Normal file
40
app/hooks/useThrottledCallback.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import throttle from "lodash/throttle";
|
||||
import * as React from "react";
|
||||
import useUnmount from "./useUnmount";
|
||||
|
||||
interface ThrottleSettings {
|
||||
leading?: boolean | undefined;
|
||||
trailing?: boolean | undefined;
|
||||
}
|
||||
|
||||
const defaultOptions: ThrottleSettings = {
|
||||
leading: false,
|
||||
trailing: true,
|
||||
};
|
||||
|
||||
/**
|
||||
* A hook that returns a throttled callback function.
|
||||
*
|
||||
* @param fn The function to throttle
|
||||
* @param wait The time in ms to wait before calling the function
|
||||
* @param dependencies The dependencies to watch for changes
|
||||
* @param options The throttle options
|
||||
*/
|
||||
export default function useThrottledCallback<T extends (...args: any[]) => any>(
|
||||
fn: T,
|
||||
wait = 250,
|
||||
dependencies: React.DependencyList = [],
|
||||
options: ThrottleSettings = defaultOptions
|
||||
) {
|
||||
const handler = React.useMemo(
|
||||
() => throttle<T>(fn, wait, options),
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
dependencies
|
||||
);
|
||||
|
||||
useUnmount(() => {
|
||||
handler.cancel();
|
||||
});
|
||||
|
||||
return handler;
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import * as React from "react";
|
||||
import useDebouncedCallback from "./useDebouncedCallback";
|
||||
import useEventListener from "./useEventListener";
|
||||
import useThrottledCallback from "./useThrottledCallback";
|
||||
|
||||
/**
|
||||
* A debounced hook that listens to the window resize event and returns the
|
||||
@@ -14,22 +14,25 @@ export default function useWindowSize() {
|
||||
height: window.innerHeight,
|
||||
});
|
||||
|
||||
const handleResize = useDebouncedCallback(() => {
|
||||
if (
|
||||
window.innerWidth !== windowSize.width ||
|
||||
window.innerHeight !== windowSize.height
|
||||
) {
|
||||
setWindowSize({
|
||||
width: window.innerWidth,
|
||||
height: window.innerHeight,
|
||||
});
|
||||
}
|
||||
const handleResize = useThrottledCallback(() => {
|
||||
setWindowSize((state) => {
|
||||
if (
|
||||
window.innerWidth === state.width &&
|
||||
window.innerHeight === state.height
|
||||
) {
|
||||
return state;
|
||||
}
|
||||
|
||||
return { width: window.innerWidth, height: window.innerHeight };
|
||||
});
|
||||
}, 100);
|
||||
|
||||
useEventListener("resize", handleResize);
|
||||
|
||||
// Call handler right away so state gets updated with initial window size
|
||||
handleResize();
|
||||
React.useEffect(() => {
|
||||
handleResize();
|
||||
}, [handleResize]);
|
||||
|
||||
return windowSize;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user