Open mikestopcontinues opened 4 years ago
Also one option could be to use useRafState
to update React's state only once per animation frame.
Here's how I'm handling the problem for window events...
// import
import {useEffect} from 'react';
import {useRafState} from 'react-use';
import throttle from 'libs/throttle';
// vars
const listenerOpts = {capture: false, passive: true};
// export
export default function useWindowEvents(fn, events = [], ms = 0) {
const [state, setState] = useRafState(fn());
useEffect(() => {
const handler = throttle(() => setState(fn()), ms, ms * 10);
events.map((e) => window.addEventListener(e, handler, listenerOpts));
return () => {
events.map((e) => window.removeEventListener(e, handler));
handler.cancel();
};
}, [fn, events, ms]);
return state;
}
And here's the throttle fn I'm using, which waits max
ms between calls while retriggering, but quickly cleans up within min
ms when retriggering ends. I see this as the ideal for event listeners...
// export
export default function throttle(fn, min = 0, max = 0) {
let id = null;
let dead = false;
let lastExec = 0;
function handler(...args) {
if (dead) {
return;
}
const elapsed = Date.now() - lastExec;
const minDiff = Math.max(0, min - elapsed);
const maxDiff = Math.max(0, max - elapsed);
clearTimeout(id);
id = setTimeout(() => {
fn(...args);
lastExec = Date.now();
id = null;
}, id && max > 0 ? maxDiff > min ? min : maxDiff : minDiff);
}
handler.cancel = () => {
clearTimeout(id);
dead = true;
};
return handler;
}
Is your feature request related to a problem? Please describe. There's quite a number of hooks in
react-use
that rely on event listeners. For example,useSize
,useWindowScroll
, etc. And when relying on them, the frequency of their updates can cause performance issues.At worst, the result will be unnecessary re-renders, but even in the best case, which requires throttling the output of the offending hooks, you end up with tons of subtree re-computations because the deep hooks still updated.
Describe the solution you'd like Ideally, you want to throttle the event listeners themselves, so that the hooks enclosing them see changes less frequently. The only additional parameter required would be a debounce-in-ms, as we can assume anything that relies on an event listener will benefit from a leading call and trailing calls at a set rate.
Describe alternatives you've considered