-
Notifications
You must be signed in to change notification settings - Fork 723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
perf(demo): lazy render example tiles #1896
base: master
Are you sure you want to change the base?
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
import React from 'react'; | ||
import React, { useEffect, useRef, useState } from 'react'; | ||
import Link from 'next/link'; | ||
import ParentSize from '@visx/responsive/lib/components/ParentSize'; | ||
import { WidthAndHeight } from '../types'; | ||
|
@@ -18,6 +18,43 @@ type Props<ExampleProps extends WidthAndHeight> = { | |
const renderLinkWrapper = (url: string | undefined, node: React.ReactNode) => | ||
url ? <Link href={url}>{node}</Link> : node; | ||
|
||
/** | ||
* hook which returns if the ref was ever visible. | ||
* used for better perf/not rendering all tiles on load. | ||
*/ | ||
function useEverVisible() { | ||
const ref = useRef(); | ||
const [everVisible, setIsVisible] = useState(false); | ||
|
||
useEffect(() => { | ||
const observer = new IntersectionObserver( | ||
(entries) => { | ||
entries.forEach((entry) => { | ||
setIsVisible((visible) => visible || entry.isIntersecting); | ||
}); | ||
}, | ||
{ | ||
root: null, // viewport is the root | ||
threshold: 0.01, | ||
}, | ||
); | ||
|
||
let curr; | ||
if (ref.current) { | ||
curr = ref.current; | ||
observer.observe(ref.current); | ||
} | ||
|
||
return () => { | ||
if (curr) { | ||
observer.unobserve(curr); | ||
} | ||
}; | ||
}, [ref]); | ||
|
||
return { everVisible, ref }; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you think about this hook instead? function useIntersectionObserver() {
const [entry, setEntry] = useState<IntersectionObserverEntry | null>(null);
const prevObserver = useRef<IntersectionObserver | null>(null);
const ref = useCallback((node: Element) => {
if (prevObserver.current) {
prevObserver.current.disconnect();
prevObserver.current = null;
}
const observer = new IntersectionObserver(
([entry]) => {
setEntry(entry);
},
{ threshold: 0.01, root: null },
);
observer.observe(node);
prevObserver.current = observer;
}, []);
return [ref, entry];
} This could be a better approach for a few reasons:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I dig it! this is a great suggestion for all the reasons you listed, I was just being lazy (hah!) :) |
||
export default function GalleryTile<ExampleProps extends WidthAndHeight>({ | ||
description, | ||
detailsHeight = 76, | ||
|
@@ -28,21 +65,24 @@ export default function GalleryTile<ExampleProps extends WidthAndHeight>({ | |
tileStyles, | ||
title, | ||
}: Props<ExampleProps>) { | ||
const { everVisible, ref } = useEverVisible(); | ||
return ( | ||
<> | ||
{renderLinkWrapper( | ||
exampleUrl, | ||
<div className="gallery-tile" style={tileStyles}> | ||
<div ref={ref} className="gallery-tile" style={tileStyles}> | ||
<div className="image"> | ||
<ParentSize> | ||
{({ width, height }) => | ||
React.createElement(exampleRenderer, { | ||
width, | ||
height: height + (title || description ? detailsHeight : 0), | ||
...exampleProps, | ||
} as ExampleProps) | ||
} | ||
</ParentSize> | ||
{everVisible && ( | ||
<ParentSize> | ||
{({ width, height }) => | ||
React.createElement(exampleRenderer, { | ||
width, | ||
height: height + (title || description ? detailsHeight : 0), | ||
...exampleProps, | ||
} as ExampleProps) | ||
} | ||
</ParentSize> | ||
)} | ||
</div> | ||
{(title || description) && ( | ||
<div className="details" style={detailsStyles}> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, might not want to use a ref as an effect dep. It'll be stable across re-renders and not trigger the effect. Can use
useCallback()
. https://legacy.reactjs.org/docs/hooks-faq.html#how-can-i-measure-a-dom-node