Skip to content

Commit

Permalink
✨ Add hydate on intersection HOC
Browse files Browse the repository at this point in the history
  • Loading branch information
TomokiMiyauci committed Sep 22, 2021
1 parent 05e56b3 commit 42d0b44
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 3 deletions.
100 changes: 100 additions & 0 deletions lib/Intersection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { useRef, createElement, useEffect } from 'react'
import { isServer } from '@/utils'
import { display, DEFAULT_PROPS } from '@/constants'
import { useFallback } from '@/hooks'
import { hydrate } from 'react-dom'
import type { ReactHTML, DetailedHTMLProps, HTMLAttributes } from 'react'

type IntersectionProps<T extends keyof ReactHTML> = {
children: JSX.Element
as?: T
/** When DOM is not exists, fallback to children or passed component */
fallback?: false | JSX.Element
/** On fallback component is rendered, then fire */
onFallback?: () => void
keepRender?: boolean
} & DetailedHTMLProps<HTMLAttributes<HTMLElement>, HTMLElement> &
IntersectionObserverInit

/**
* Lazy hydration component until intersection
*
* @example
* ```tsx
* <Intersection>
* <div>This do not hydrate</div>
* </Intersection>
* ```
*/
const Intersection = <T extends keyof ReactHTML>({
children,
fallback = children,
as,
onFallback,
style,
keepRender = true,
root,
rootMargin,
threshold,
...props
}: IntersectionProps<T>) => {
const ref = useRef<HTMLDivElement>(null)
const _as = as ?? 'div'

useFallback(
ref,
{
fallback
},
[]
)

useEffect(() => {
if (!ref.current) return

const observer = new IntersectionObserver(
([entry], obs) => {
if (entry.isIntersecting) {
hydrate(children, ref.current)
if (keepRender && ref.current) {
obs.unobserve(ref.current)
}
} else {
// keep
}
},
{ root, rootMargin, threshold }
)

if (ref.current) {
observer.observe(ref.current)
}

return () => observer.disconnect()
}, [root, rootMargin, threshold])

if (isServer)
return createElement(
_as,
{
style: {
display,
...style
},
...props
},
children
)

return createElement(_as, {
ref,
style: {
display,
...style
},
...props,
...DEFAULT_PROPS
})
}

export default Intersection
4 changes: 2 additions & 2 deletions lib/Static.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { display, DEFAULT_PROPS } from '@/constants'
import { useFallback } from '@/hooks'
import type { ReactHTML, DetailedHTMLProps, HTMLAttributes } from 'react'

type Props<T extends keyof ReactHTML> = {
type StaticProps<T extends keyof ReactHTML> = {
children: JSX.Element
as?: T
/** When DOM is not exists, fallback to children or passed component */
Expand All @@ -30,7 +30,7 @@ const Static = <T extends keyof ReactHTML>({
onFallback,
style,
...props
}: Props<T>) => {
}: StaticProps<T>) => {
const ref = useRef<HTMLDivElement>(null)
const _as = as ?? 'div'

Expand Down
3 changes: 2 additions & 1 deletion lib/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Static from '@/Static'
import Intersection from '@/Intersection'
import { useFallback } from '@/hooks'
export { Static, useFallback }
export { Static, Intersection, useFallback }

0 comments on commit 42d0b44

Please sign in to comment.