diff --git a/examples/magento-graphcms/components/GraphCMS/RowRenderer.tsx b/examples/magento-graphcms/components/GraphCMS/RowRenderer.tsx index cf1bf624174..ee2973bb005 100644 --- a/examples/magento-graphcms/components/GraphCMS/RowRenderer.tsx +++ b/examples/magento-graphcms/components/GraphCMS/RowRenderer.tsx @@ -43,8 +43,8 @@ export function RowRenderer(props: PageProps) { return ( <> {content?.map((item, index) => ( - - + + ))} diff --git a/examples/magento-graphcms/components/Layout/LayoutNavigation.tsx b/examples/magento-graphcms/components/Layout/LayoutNavigation.tsx index 47dcaf024f7..e4e21b48717 100644 --- a/examples/magento-graphcms/components/Layout/LayoutNavigation.tsx +++ b/examples/magento-graphcms/components/Layout/LayoutNavigation.tsx @@ -41,12 +41,9 @@ export function LayoutNavigation(props: LayoutNavigationProps) { const selection = useNavigationSelection() const router = useRouter() - const [hold, setHold] = useState(true) - const i = () => selection.set([menu?.items?.[0]?.uid || '']) return ( <> [ @@ -131,12 +128,7 @@ export function LayoutNavigation(props: LayoutNavigationProps) { ))} - { - setHold(false) - setTimeout(() => selection.set([menu?.items?.[0]?.uid || '']), 100) - }} - > + selection.set([menu?.items?.[0]?.uid || ''])}> {menu?.items?.[0]?.name} diff --git a/packages/magento-product/components/ProductListItems/ProductListItemsBase.tsx b/packages/magento-product/components/ProductListItems/ProductListItemsBase.tsx index b15d5271b6f..7523c932a12 100644 --- a/packages/magento-product/components/ProductListItems/ProductListItemsBase.tsx +++ b/packages/magento-product/components/ProductListItems/ProductListItemsBase.tsx @@ -65,9 +65,8 @@ export function ProductListItemsBase(props: ProductItemsGridProps) { > {items?.map((item, idx) => item ? ( - idx}> + idx}> - forceHydration?: boolean - hold?: boolean - afterHydrate?: () => void -} - -function withHydrationOnDemandServerSide

(options: WithHydrationOnDemandOptions) { - const { wrapperProps } = options +function withHydrationOnDemandServerSide

() { return (Component: React.ComponentType

) => (props: P) => ( -

+
) } -function withHydrationOnDemandClientSide

(incoming: WithHydrationOnDemandOptions) { - const { wrapperProps, forceHydration = false } = incoming - +function withHydrationOnDemandClientSide

() { return (Component: React.ComponentType

) => { - function ComponentWithHydration( - props: P & { forceHydration?: boolean; hold?: boolean; afterHydrate?: () => void }, - ) { - const { hold, afterHydrate } = props + function ComponentWithHydration(props: P & { hydrateManually?: MotionValue }) { + const { hydrateManually } = props const rootRef = useRef(null) - const isInputPending = () => { - // @ts-expect-error navigator.scheduling is not defined in the types - const isPending = navigator?.scheduling?.isInputPending?.() - return isPending ?? true - } - - const getDefaultHydrationState = () => { - const isNotInputPending = false && !isInputPending() - return isNotInputPending || forceHydration - } - - const [isHydrated, setIsHydrated] = useState(getDefaultHydrationState()) - - const hydrate = useCallback(() => { - startTransition(() => { - setIsHydrated(true) - if (afterHydrate) afterHydrate() - }) - }, [afterHydrate]) + const [isHydrated, setIsHydrated] = useState(hydrateManually?.get()) useLayoutEffect(() => { - if (hold) return - - if (isHydrated) return - - if (forceHydration) { - hydrate() - return + // If we are manually hydrating, we watch that value and do not use the IntersectionObserver + if (isHydrated || !rootRef.current) return undefined + const wasRenderedServerSide = rootRef.current?.hasAttribute('data-lazy-hydrate') + if (!wasRenderedServerSide) { + setIsHydrated(true) + return undefined } - const wasRenderedServerSide = !!rootRef.current?.getAttribute('data-hydration-on-demand') - const shouldHydrate = !wasRenderedServerSide && !false - - if (shouldHydrate) hydrate() - }, [forceHydration, hold]) - - useEffect(() => { - if (isHydrated || !rootRef.current || hold) return undefined + hydrateManually?.on('change', (val) => val && setIsHydrated(val)) + if (hydrateManually) return undefined const observer = new IntersectionObserver( ([entry]) => { if (entry.isIntersecting && entry.intersectionRatio > 0) { - return hydrate() + startTransition(() => setIsHydrated(true)) } }, { rootMargin: '200px' }, ) - observer.observe(rootRef.current) + return () => observer.disconnect() - }, [hydrate, isHydrated]) + }, [hydrateManually, isHydrated]) return !isHydrated ? (

(incoming: WithHydrati // eslint-disable-next-line react/no-danger dangerouslySetInnerHTML={{ __html: '' }} suppressHydrationWarning - {...wrapperProps} /> ) : ( -
+
) } - if (process.env.NODE_ENV !== 'production') { - ComponentWithHydration.displayName = `withHydrationOnDemand(${ - Component.displayName ?? Component.name ?? 'Component' - })` - } - return ComponentWithHydration } } export function lazyHydrate

(Component: React.ComponentType

) { return typeof window !== 'undefined' - ? withHydrationOnDemandClientSide

({})(Component) - : withHydrationOnDemandServerSide

({})(Component) + ? withHydrationOnDemandClientSide

()(Component) + : withHydrationOnDemandServerSide

()(Component) } export type LazyHydrateProps = { @@ -122,21 +72,12 @@ export type LazyHydrateProps = { * When eager is set to true, it disables all functionality and render the component regularly */ eager?: boolean - /** - * When hold is set to true, render nothing - */ - hold?: boolean - afterHydrate?: () => void + hydrateManually?: MotionValue } -export function LazyHydrate(props: { - children: React.ReactNode - eager?: boolean - hold?: boolean - afterHydrate?: () => void -}) { - const { children, eager = false, hold = false, afterHydrate } = props +export function LazyHydrate(props: LazyHydrateProps) { + const { children, eager = false, hydrateManually } = props const LazyComponent = lazyHydrate(() => children) - return eager ? children : + return eager ? children : } diff --git a/packages/next-ui/Navigation/components/NavigationOverlay.tsx b/packages/next-ui/Navigation/components/NavigationOverlay.tsx index e7339956ac4..20f9c95f19c 100644 --- a/packages/next-ui/Navigation/components/NavigationOverlay.tsx +++ b/packages/next-ui/Navigation/components/NavigationOverlay.tsx @@ -77,10 +77,6 @@ export const NavigationOverlay = React.memo((props: NavigationOverlayProps) => { c ? false : s !== false, ) - useEffect(() => { - animating.set(true) - }, [activeAndNotClosing, animating]) - const afterClose = useEventCallback(() => { if (!closing.get()) return setTimeout(() => { diff --git a/packages/next-ui/Navigation/components/NavigationProvider.tsx b/packages/next-ui/Navigation/components/NavigationProvider.tsx index e6133343d49..0631a270882 100644 --- a/packages/next-ui/Navigation/components/NavigationProvider.tsx +++ b/packages/next-ui/Navigation/components/NavigationProvider.tsx @@ -1,7 +1,8 @@ -import { MotionConfig, useMotionValue } from 'framer-motion' +import { MotionConfig, useMotionValue, useTransform } from 'framer-motion' import React, { useMemo } from 'react' import { isElement } from 'react-is' import { LazyHydrate } from '../../LazyHydrate' +import { nonNullable } from '../../RenderType/nonNullable' import { NavigationNode, NavigationContextType, @@ -22,7 +23,6 @@ export type NavigationProviderBaseProps = { } export type NavigationProviderProps = NavigationProviderBaseProps & { hold?: boolean } -const nonNullable = (value: T): value is NonNullable => value !== null && value !== undefined const NavigationProviderBase = React.memo((props) => { const { @@ -38,8 +38,6 @@ const NavigationProviderBase = React.memo((props) = const animating = useMotionValue(false) const closing = useMotionValue(false) - console.log('joe') - const value = useMemo( () => ({ hideRootOnNavigate, @@ -79,9 +77,11 @@ const NavigationProviderBase = React.memo((props) = }) export function NavigationProvider(props: NavigationProviderProps) { - const { hold = false } = props + const { selection } = props + const hydrateManually = useTransform(selection, (s) => s !== false) + return ( - + )