From 04d045db49cdbc8701695fbaca558ca0b2432670 Mon Sep 17 00:00:00 2001 From: Fanny Chien Date: Mon, 28 Oct 2024 11:50:35 -0300 Subject: [PATCH] Feat: adds LazyLoadingSection component - perf (#2521) ## What's the purpose of this pull request? Adds `LazyLoadingSection` component, it will be responsible for lazy loading Sections that are out of the viewport. * It achieves this by: * 1. Using the IntersectionObserver API for Sections below the fold. * 2. Checking the UI context for Sections that are not in the viewport, such as the CartSidebar and RegionModal. We will be implementing this in the `RenderSectionsBase` as part of our upcoming tasks. This component is not being applied in isolation due to concerns about increased Cumulative Layout Shift (CLS). ## How to test it? 1. You can use this [preview link](https://starter-git-feat-lazy-loading-section-sfs-1508-faststore.vercel.app/), where the `LazyLoadingSection` is temporary applied or locally add it in `RenderSectionsBase` component. ``` ``` 2. Inspect the code, use the React extension for this test: - search for `Footer`, you won't find it until you scroll the page until the `Newsletter` section image image https://github.com/user-attachments/assets/bbfffbe0-39b8-4b6b-b86d-04d4a7ab1e6b ### Starters Deploy Preview https://github.com/vtex-sites/starter.store/pull/586 ## References https://github.com/vtex/faststore/pull/2404 --------- Co-authored-by: Pedro Soares <32311264+pedromtec@users.noreply.github.com> --- .../src/components/cms/RenderSections.tsx | 50 +++++++++++++++++-- .../src/components/sections/Hero/Hero.tsx | 1 + 2 files changed, 48 insertions(+), 3 deletions(-) diff --git a/packages/core/src/components/cms/RenderSections.tsx b/packages/core/src/components/cms/RenderSections.tsx index fdd7d9ef2d..746fd38912 100644 --- a/packages/core/src/components/cms/RenderSections.tsx +++ b/packages/core/src/components/cms/RenderSections.tsx @@ -1,13 +1,24 @@ -import { ComponentType, PropsWithChildren, memo, useMemo } from 'react' +import { + ComponentType, + PropsWithChildren, + ReactNode, + memo, + useMemo, +} from 'react' -import SectionBoundary from './SectionBoundary' import { Section } from '@vtex/client-cms' +import SectionBoundary from './SectionBoundary' +import ViewportObserver from './ViewportObserver' + +import { useUI } from '@faststore/ui' interface Props { components: Record> sections: Array<{ name: string; data: any }> } +const SECTIONS_OUT_OF_VIEWPORT = ['CartSidebar', 'RegionModal'] + const useDividedSections = (sections: Section[]) => { return useMemo(() => { const indexChildren = sections.findIndex(({ name }) => name === 'Children') @@ -21,6 +32,39 @@ const useDividedSections = (sections: Section[]) => { }, [sections]) } +/** + * This component is responsible for lazy loading Sections that are out of the viewport. + * It achieves this by: + * 1. Using the IntersectionObserver API for Sections below the fold. + * 2. Checking the UI context for Sections that are not in the viewport, such as the CartSidebar and RegionModal. + * + * @param sectionName + * @returns + */ +export const LazyLoadingSection = ({ + sectionName, + children, +}: { + sectionName: string + children: ReactNode +}) => { + const { cart: displayCart, modal: displayModal } = useUI() + + if (SECTIONS_OUT_OF_VIEWPORT.includes(sectionName)) { + const shouldLoad = + (sectionName === 'CartSidebar' && displayCart) || + (sectionName === 'RegionModal' && displayModal) + if (!shouldLoad) { + return null + } + + return children + } + return ( + {children} + ) +} + const RenderSectionsBase = ({ sections = [], components }: Props) => { return ( <> @@ -37,7 +81,7 @@ const RenderSectionsBase = ({ sections = [], components }: Props) => { } return ( - + ) diff --git a/packages/core/src/components/sections/Hero/Hero.tsx b/packages/core/src/components/sections/Hero/Hero.tsx index c98c26116d..85987474d6 100644 --- a/packages/core/src/components/sections/Hero/Hero.tsx +++ b/packages/core/src/components/sections/Hero/Hero.tsx @@ -60,6 +60,7 @@ const Hero = ({ width={360} height={240} sizes="(max-width: 412px) 40vw, (max-width: 768px) 90vw, 50vw" + priority={true} />