Files
payload/packages/ui/src/hooks/useDelayedRender.ts
Alessio Gravili ebd43c7763 feat: pre-compile ui and richtext-lexical with react compiler (#7688)
This noticeably improves performance in the admin panel, for example
when there are multiple richtext editors on one page (& likely
performance in other areas too, though I mainly tested rich text).

The babel plugin currently only optimizes files with a 'use client'
directive at the top - thus we have to make sure to add use client
wherever possible, even if it's imported by a parent client component.

There's one single component that broke when it was compiled using the
React compiler (it stopped being reactive and failed one of our admin
e2e tests):
150808f608
opting out of it completely fixed that issue

Fixes https://github.com/payloadcms/payload/issues/7366
2024-08-19 17:31:36 -04:00

71 lines
2.3 KiB
TypeScript

'use client'
import * as React from 'react'
import { useDelay } from './useDelay.js'
type DelayedRenderProps = {
/** Time in ms to wait before "mounting" the component. */
delayBeforeShow: number
/** Time in ms for the "enter" phase of the transition, after delay completes. */
inTimeout: number
/** Min time in ms for the "entered" phase of the transition. */
minShowTime: number
/** Time in ms for the exit phase of the transition. */
outTimeout: number
/** `true` starts the mount process.
* `false` starts the unmount process.
* */
show: boolean
}
type useDelayedRenderT = (props: DelayedRenderProps) => {
/** `true` if the component has mounted after the timeout. */
isMounted: boolean
/** `true` if the component is unmounting. */
isUnmounting: boolean
/** Call this function to trigger the timeout delay before rendering. */
triggerDelayedRender: () => void
}
export const useDelayedRender: useDelayedRenderT = ({
delayBeforeShow,
inTimeout,
minShowTime,
outTimeout,
show,
}) => {
const totalMountTime = inTimeout + minShowTime + outTimeout
const [hasDelayed, triggerDelay] = useDelay(delayBeforeShow)
const [isMounted, setIsMounted] = React.useState(false)
const [isUnmounting, setIsUnmounting] = React.useState(false)
const onMountTimestampRef = React.useRef(0)
const unmountTimeoutRef: React.RefObject<NodeJS.Timeout | undefined> = React.useRef(undefined)
const unmount = React.useCallback(() => {
setIsUnmounting(true)
unmountTimeoutRef.current = setTimeout(() => {
setIsMounted(false)
setIsUnmounting(false)
}, outTimeout)
}, [setIsUnmounting, outTimeout])
React.useEffect(() => {
const shouldMount = hasDelayed && !isMounted && show
const shouldUnmount = isMounted && !show
if (shouldMount) {
onMountTimestampRef.current = Date.now()
setIsMounted(true)
} else if (shouldUnmount) {
const totalTimeMounted = Date.now() - onMountTimestampRef.current
const remainingTime = totalMountTime - totalTimeMounted
clearTimeout(unmountTimeoutRef.current)
unmountTimeoutRef.current = setTimeout(unmount, Math.max(0, remainingTime))
}
}, [isMounted, show, unmount, totalMountTime, hasDelayed])
return {
isMounted,
isUnmounting,
triggerDelayedRender: triggerDelay,
}
}