Frontend Development 9 min read

Optimizing Long Page Rendering in React: Lazy Loading, Intersection Observer, and Memoization

This article explains how to improve performance of long React pages by rendering components screen‑by‑screen using viewport detection (getBoundingClientRect or Intersection Observer), grouping data, debouncing scroll handlers, and applying React.memo to prevent duplicate data requests.

ByteFE
ByteFE
ByteFE
Optimizing Long Page Rendering in React: Lazy Loading, Intersection Observer, and Memoization

Background: Long pages are common in front‑end development; rendering the whole page at once harms first‑meaningful‑paint and first‑input‑delay.

Design: Render components screen by screen as the user scrolls. Key questions are when to render the next screen and how to avoid duplicate data requests.

Timing detection: Use a loading placeholder at the bottom of the list. When the placeholder enters the viewport, trigger rendering of the next group. Two methods are used: (1) Element.getBoundingClientRect() to compare the element’s top with the viewport height; (2) the Intersection Observer API (or react-intersection-observer ).

Implementation of the getBoundingClientRect method:

const scrollRenderHandler = (): void => {
  const rect = bottomDomRef.current?.getBoundingClientRect();
  const top = rect ? rect.top : 0;
  const clientHeight = document.documentElement.clientHeight || document.body.clientHeight;
  if (top < clientHeight && /* not all components rendered */) {
    // render next group
  }
};
useEffect(() => {
  document.addEventListener('scroll', scrollRenderHandler);
  return () => {
    document.removeEventListener('scroll', scrollRenderHandler);
  };
}, [scrollRenderHandler]);

Intersection Observer example using react-intersection-observer :

import { useInView } from 'react-intersection-observer';
const [bottomDomRef, inView] = useInView({ threshold: 0 });
const scrollRenderHandler = (): void => {
  if (inView && /* not all components rendered */) {
    // render next group
  }
};

Component grouping: Split the data list into groups (e.g., pageSize = 3) and render each group sequentially, tracking the current group index.

export const splitGroups = (homeList: any[], pageSize: number): any[] => {
  const groupsTemp = [];
  for (let i = 0; i < homeList.length; i += pageSize) {
    groupsTemp.push(homeList.slice(i, i + pageSize));
  }
  return groupsTemp;
};
const compGroups = useMemo(() => splitGroups(homeInfo, 3), [homeInfo]);
const [groupIdx, setGroupIdx] = useState(0);
if (top < clientHeight && groupIdx < compGroups.length) {
  setCompList(compList.concat(compGroups[groupIdx]));
  setGroupIdx(groupIdx + 1);
}

Scroll optimization: Debounce the scroll handler with useDebounce (or useCallback ) to reduce the frequency of executions.

const [scrollRenderHandler] = useDebounce((): void => {
  if (inView && groupIdx < groupCount) {
    setCompList(compList.concat(compGroups[groupIdx]));
    setGroupIdx(groupIdx + 1);
  }
}, 300, [compGroups, compList, groupIdx, inView]);

Avoiding duplicate requests: Wrap each floor component with React.memo (or PureComponent ) and compare a unique identifier (uuid) to prevent re‑rendering and repeated API calls.

function MyComponent(props) { /* render using props */ }
function areEqual(prevProps, nextProps) {
  return prevProps.goodsQuery.uuid === nextProps.goodsQuery.uuid;
}
export default React.memo(MyComponent, areEqual);

Summary: By combining viewport detection, group‑wise rendering, debounced scroll handling, and memoization, a long React page can achieve smooth scrolling, reduced network traffic, and better performance metrics.

performance optimizationReactlazy loadinguseCallbackuseMemointersection observerReact.memo
ByteFE
Written by

ByteFE

Cutting‑edge tech, article sharing, and practical insights from the ByteDance frontend team.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.