## Description In order to improve the first load of the applications, now we're only rendering the widget components that are [above the fold](https://en.wikipedia.org/wiki/Above_the_fold#In_web_design) right away and rendering the other widget components whenever the browser is idle. This decreases the amount of time it takes before the first paint on the screen. This is getting shipped behind a feature flag!
67 lines
2.1 KiB
TypeScript
67 lines
2.1 KiB
TypeScript
import { useEffect, useRef, useState } from "react";
|
|
import React from "react";
|
|
import BaseWidget, { WidgetProps } from "./BaseWidget";
|
|
import { REQUEST_IDLE_CALLBACK_TIMEOUT } from "constants/AppConstants";
|
|
import { useSelector } from "react-redux";
|
|
import { selectFeatureFlags } from "selectors/usersSelectors";
|
|
|
|
export function withLazyRender(Widget: typeof BaseWidget) {
|
|
return function WrappedComponent(props: WidgetProps) {
|
|
const features = useSelector(selectFeatureFlags);
|
|
const [deferRender, setDeferRender] = useState(
|
|
!!features.LAZY_CANVAS_RENDERING,
|
|
);
|
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
|
let idleCallbackId: number;
|
|
let observer: IntersectionObserver;
|
|
|
|
useEffect(() => {
|
|
if (wrapperRef.current && deferRender) {
|
|
/*
|
|
* For the hidden widgets, we are observing till it,
|
|
* 1. Scrolls into view, or
|
|
* 2. idleCallback is called (browser is either idle or timed out)
|
|
* which ever happens first
|
|
*/
|
|
|
|
observer = new IntersectionObserver(
|
|
(entries: IntersectionObserverEntry[]) => {
|
|
if (!!entries.find((entry) => entry.isIntersecting)) {
|
|
setDeferRender(false);
|
|
(window as any).cancelIdleCallback(idleCallbackId);
|
|
observer.disconnect();
|
|
} else if (!idleCallbackId) {
|
|
idleCallbackId = (window as any).requestIdleCallback(
|
|
() => {
|
|
setDeferRender(false);
|
|
observer.disconnect();
|
|
},
|
|
{
|
|
timeout: REQUEST_IDLE_CALLBACK_TIMEOUT.lowPriority,
|
|
},
|
|
);
|
|
}
|
|
},
|
|
{
|
|
root: null,
|
|
threshold: 0,
|
|
},
|
|
);
|
|
|
|
observer.observe(wrapperRef.current);
|
|
} else {
|
|
setDeferRender(false);
|
|
}
|
|
|
|
return () => {
|
|
(window as any).cancelIdleCallback(idleCallbackId);
|
|
observer.disconnect();
|
|
};
|
|
}, []);
|
|
|
|
return (
|
|
<Widget {...props} deferRender={deferRender} wrapperRef={wrapperRef} />
|
|
);
|
|
};
|
|
}
|