Versions: react-remove-scroll 2.7.2 (latest), consumed via @radix-ui/react-dialog 1.1.15, React 18.2, Chrome/Chromium.
Error
TypeError: Failed to execute 'getComputedStyle' on 'Window': parameter 1 is not of type 'Element'.
at handleScroll (react-remove-scroll/dist/.../handleScroll.js)
at shouldCancelEvent (SideEffect: handleScroll(cancelingAxis, parent, event, ...))
at scrollWheel / onScrollCapture (React-dispatched)
Mechanism
SideEffect's React-attached capture handlers call shouldCancelEvent(event, props.lockRef.current), and handleScroll immediately does:
const directionFactor = getDirectionFactor(axis, window.getComputedStyle(endTarget).direction);
with no guard on endTarget. When the component owning the scroll lock is unmounted abruptly while open — the concrete case: a Radix Dialog open when an SPA route change unmounts the page containing it — a scroll/wheel event can still be dispatched through the React tree during teardown after lockRef.current has already been nulled, so getComputedStyle(null) throws.
This is distinct from #108: that report was about ShadowRoot nodes reaching elementCanBeScrolled (and the guard added there fixed it). Here the very first getComputedStyle call in handleScroll receives null, before any of the guarded helpers run.
Reproduction sketch
- React app with client-side routing (React Router), Radix Dialog (modal, default
RemoveScroll integration).
- Open the dialog.
- Navigate to another route via a
<Link> inside the open dialog (so the dialog unmounts abruptly while open, no exit transition).
- Console shows the TypeError (in our app it fires twice per navigation, from the scroll and wheel capture paths). Reproducible in both dev and production bundles.
A graceful controlled close (open={false}, exit animation completes) never throws — only unmount-while-open does.
Impact
Functionally near-harmless (the throw happens in a React-guarded event handler during teardown), but it lands in window.onerror, so it pollutes error monitoring (one ingested error per dialog→navigation) and fails any console-clean assertion in e2e suites. "Close before navigating" is not a viable userland workaround: React 18 batches the close and the navigation state updates into one commit, so the unmount is abrupt either way.
Suggested fix
A null/Element guard at the top of handleScroll (or in shouldCancelEvent before calling it), e.g.:
export const handleScroll = (axis, endTarget, event, sourceDelta, noOverscroll) => {
if (!(endTarget instanceof Element)) {
return true; // conservative: treat as "should cancel" so lock semantics don't loosen mid-teardown
}
...
Happy to open a PR if the conservative return value is the behavior you'd want.
Versions: react-remove-scroll 2.7.2 (latest), consumed via @radix-ui/react-dialog 1.1.15, React 18.2, Chrome/Chromium.
Error
Mechanism
SideEffect's React-attached capture handlers callshouldCancelEvent(event, props.lockRef.current), andhandleScrollimmediately does:with no guard on
endTarget. When the component owning the scroll lock is unmounted abruptly while open — the concrete case: a Radix Dialog open when an SPA route change unmounts the page containing it — a scroll/wheel event can still be dispatched through the React tree during teardown afterlockRef.currenthas already been nulled, sogetComputedStyle(null)throws.This is distinct from #108: that report was about ShadowRoot nodes reaching
elementCanBeScrolled(and the guard added there fixed it). Here the very firstgetComputedStylecall inhandleScrollreceivesnull, before any of the guarded helpers run.Reproduction sketch
RemoveScrollintegration).<Link>inside the open dialog (so the dialog unmounts abruptly while open, no exit transition).A graceful controlled close (
open={false}, exit animation completes) never throws — only unmount-while-open does.Impact
Functionally near-harmless (the throw happens in a React-guarded event handler during teardown), but it lands in
window.onerror, so it pollutes error monitoring (one ingested error per dialog→navigation) and fails any console-clean assertion in e2e suites. "Close before navigating" is not a viable userland workaround: React 18 batches the close and the navigation state updates into one commit, so the unmount is abrupt either way.Suggested fix
A null/Element guard at the top of
handleScroll(or inshouldCancelEventbefore calling it), e.g.:Happy to open a PR if the conservative return value is the behavior you'd want.