Skip to content

Commit f231275

Browse files
committed
fix: always hydrating when there are no elements #9
avoid creating intersection observer for each instance
1 parent 780a653 commit f231275

File tree

1 file changed

+33
-30
lines changed

1 file changed

+33
-30
lines changed

src/index.tsx

Lines changed: 33 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,26 @@ const isBrowser =
1717
typeof window.document !== "undefined" &&
1818
typeof window.document.createElement !== "undefined";
1919

20+
const event = "hydrate";
21+
22+
const io =
23+
isBrowser && IntersectionObserver
24+
? new IntersectionObserver(entries => {
25+
entries.forEach(entry => {
26+
if (entry.isIntersecting || entry.intersectionRatio > 0) {
27+
entry.target.dispatchEvent(new CustomEvent(event));
28+
}
29+
});
30+
})
31+
: null;
32+
2033
// React currently throws a warning when using useLayoutEffect on the server.
2134
const useIsomorphicLayoutEffect = isBrowser
2235
? React.useLayoutEffect
2336
: React.useEffect;
2437

2538
const LazyHydrate: React.FunctionComponent<Props> = function(props) {
2639
const childRef = React.useRef<HTMLDivElement>(null);
27-
const cleanupFns = React.useRef<VoidFunction[]>([]);
28-
const io = React.useRef<IntersectionObserver>(null);
2940

3041
// Always render on server
3142
const [hydrated, setHydrated] = React.useState(!isBrowser);
@@ -47,16 +58,17 @@ const LazyHydrate: React.FunctionComponent<Props> = function(props) {
4758

4859
useIsomorphicLayoutEffect(() => {
4960
// No SSR Content
50-
if (childRef.current.childElementCount === 0) {
61+
if (!childRef.current.hasChildNodes()) {
5162
setHydrated(true);
5263
}
5364
}, []);
5465

5566
React.useEffect(() => {
5667
if (ssrOnly || hydrated) return;
68+
const cleanupFns: VoidFunction[] = [];
5769
function cleanup() {
58-
while (cleanupFns.current.length) {
59-
cleanupFns.current.pop()();
70+
while (cleanupFns.length) {
71+
cleanupFns.pop()();
6072
}
6173
}
6274
function hydrate() {
@@ -67,52 +79,43 @@ const LazyHydrate: React.FunctionComponent<Props> = function(props) {
6779
// @ts-ignore
6880
if (requestIdleCallback) {
6981
// @ts-ignore
70-
const idleCallbackId = requestIdleCallback(
71-
() => requestAnimationFrame(() => hydrate()),
72-
{ timeout: 500 }
73-
);
74-
cleanupFns.current.push(() => {
82+
const idleCallbackId = requestIdleCallback(hydrate, { timeout: 500 });
83+
cleanupFns.push(() => {
7584
// @ts-ignore
7685
cancelIdleCallback(idleCallbackId);
7786
});
7887
} else {
79-
setTimeout(hydrate, 2000);
88+
const id = setTimeout(hydrate, 2000);
89+
cleanupFns.push(() => {
90+
clearTimeout(id);
91+
});
8092
}
8193
}
8294

95+
let events = Array.isArray(on) ? on.slice() : [on];
96+
8397
if (whenVisible) {
84-
if (io.current === null && IntersectionObserver) {
85-
io.current = new IntersectionObserver(entries => {
86-
entries.forEach(entry => {
87-
if (
88-
entry.target.parentElement === childRef.current &&
89-
(entry.isIntersecting || entry.intersectionRatio > 0)
90-
) {
91-
hydrate();
92-
}
93-
});
94-
});
95-
}
96-
if (io.current && childRef.current.childElementCount !== 0) {
98+
if (io && childRef.current.childElementCount) {
9799
// As root node does not have any box model, it cannot intersect.
98-
io.current.observe(childRef.current.children[0]);
99-
cleanupFns.current.push(() => {
100-
io.current.unobserve(childRef.current.children[0]);
100+
const el = childRef.current.children[0];
101+
io.observe(el);
102+
events.push(event as keyof HTMLElementEventMap);
103+
104+
cleanupFns.push(() => {
105+
io.unobserve(el);
101106
});
102107
} else {
103108
return hydrate();
104109
}
105110
}
106111

107-
const events = Array.isArray(on) ? on : [on];
108-
109112
events.forEach(event => {
110113
childRef.current.addEventListener(event, hydrate, {
111114
once: true,
112115
capture: true,
113116
passive: true
114117
});
115-
cleanupFns.current.push(() => {
118+
cleanupFns.push(() => {
116119
childRef.current.removeEventListener(event, hydrate, { capture: true });
117120
});
118121
});

0 commit comments

Comments
 (0)