diff --git a/src/index.tsx b/src/index.tsx index c686eb9..9a876fb 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -2,6 +2,25 @@ import * as React from "react"; import { isBrowser, isDev } from "./constants.macro"; +type RequestIdleCallbackHandle = number; +type RequestIdleCallbackOptions = { + timeout: number; +}; +type RequestIdleCallbackDeadline = { + readonly didTimeout: boolean; + timeRemaining: () => number; +}; + +declare global { + interface Window { + requestIdleCallback?: ( + callback: (deadline: RequestIdleCallbackDeadline) => void, + opts?: RequestIdleCallbackOptions + ) => RequestIdleCallbackHandle; + cancelIdleCallback?: (handle: RequestIdleCallbackHandle) => void; + } +} + export type LazyProps = { ssrOnly?: boolean; whenIdle?: boolean; @@ -40,7 +59,7 @@ const useIsomorphicLayoutEffect = isBrowser ? React.useLayoutEffect : React.useEffect; -function LazyHydrate(props: Props) { +const LazyHydrate: React.FunctionComponent = function(props) { const childRef = React.useRef(null); // Always render on server @@ -74,7 +93,7 @@ function LazyHydrate(props: Props) { useIsomorphicLayoutEffect(() => { // No SSR Content - if (!childRef.current.hasChildNodes()) { + if (!childRef.current?.hasChildNodes()) { setHydrated(true); } }, []); @@ -84,7 +103,7 @@ function LazyHydrate(props: Props) { const cleanupFns: VoidFunction[] = []; function cleanup() { while (cleanupFns.length) { - cleanupFns.pop()(); + cleanupFns.pop()!(); } } function hydrate() { @@ -97,13 +116,15 @@ function LazyHydrate(props: Props) { } if (whenIdle) { - // @ts-ignore - if (typeof requestIdleCallback !== "undefined") { - // @ts-ignore - const idleCallbackId = requestIdleCallback(hydrate, { timeout: 500 }); + if (typeof window !== "undefined" && window.requestIdleCallback) { + const idleCallbackId = window.requestIdleCallback(hydrate, { + timeout: 500 + }); + cleanupFns.push(() => { - // @ts-ignore - cancelIdleCallback(idleCallbackId); + if (window.cancelIdleCallback) { + window.cancelIdleCallback(idleCallbackId); + } }); } else { const id = setTimeout(hydrate, 2000); @@ -116,7 +137,7 @@ function LazyHydrate(props: Props) { let events = Array.isArray(on) ? on.slice() : [on]; if (whenVisible) { - if (io && childRef.current.childElementCount) { + if (io && childRef.current?.childElementCount) { // As root node does not have any box model, it cannot intersect. const el = childRef.current.children[0]; io.observe(el); @@ -131,13 +152,15 @@ function LazyHydrate(props: Props) { } events.forEach(event => { - childRef.current.addEventListener(event, hydrate, { + childRef.current?.addEventListener(event, hydrate, { once: true, capture: true, passive: true }); cleanupFns.push(() => { - childRef.current.removeEventListener(event, hydrate, { capture: true }); + childRef.current?.removeEventListener(event, hydrate, { + capture: true + }); }); }); @@ -146,7 +169,7 @@ function LazyHydrate(props: Props) { if (hydrated) { if (noWrapper) { - return children; + return <>{children}; } return (
@@ -164,6 +187,6 @@ function LazyHydrate(props: Props) { /> ); } -} +}; export default LazyHydrate; diff --git a/tsconfig.json b/tsconfig.json index ccbdd31..e5310cf 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,6 +4,7 @@ "declaration": true, "declarationDir": "./types", "emitDeclarationOnly": true, - "jsx": "preserve" + "jsx": "preserve", + "strict": true } }