Skip to content

Commit

Permalink
calculate the size of an element as soon as possible (#3153)
Browse files Browse the repository at this point in the history
Instead of waiting for a `useEffect` to trigger, we can use `useMemo` to
always compute the size the moment a DOM element is available (or
changes).

We also make sure to force a re-render when resizes are detected on the
stable DOM node, which in turn causes the `useMemo` to recompute.
  • Loading branch information
RobinMalfait committed Apr 30, 2024
1 parent 319bcba commit f35214d
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 6 deletions.
Expand Up @@ -1644,6 +1644,7 @@ function OptionsFn<TTag extends ElementType = typeof DEFAULT_OPTIONS_TAG>(
option: undefined,
} satisfies OptionsRenderPropArg
}, [data])

let ourProps = mergeProps(anchor ? getFloatingPanelProps() : {}, {
'aria-labelledby': labelledBy,
role: 'listbox',
Expand Down
16 changes: 10 additions & 6 deletions packages/@headlessui-react/src/hooks/use-element-size.ts
@@ -1,4 +1,4 @@
import { useState } from 'react'
import { useMemo, useReducer } from 'react'
import { useIsoMorphicEffect } from './use-iso-morphic-effect'

function computeSize(element: HTMLElement | null) {
Expand All @@ -12,15 +12,19 @@ export function useElementSize(
unit = false
) {
let element = ref === null ? null : 'current' in ref ? ref.current : ref
let [size, setSize] = useState(() => computeSize(element))
let [identity, forceRerender] = useReducer(() => ({}), {})

// When the element changes during a re-render, we want to make sure we
// compute the correct size as soon as possible. However, once the element is
// stable, we also want to watch for changes to the element. The `identity`
// state can be used to recompute the size.
let size = useMemo(() => computeSize(element), [element, identity])

useIsoMorphicEffect(() => {
if (!element) return

let observer = new ResizeObserver(() => {
setSize(computeSize(element))
})

// Trigger a re-render whenever the element resizes
let observer = new ResizeObserver(forceRerender)
observer.observe(element)

return () => {
Expand Down

0 comments on commit f35214d

Please sign in to comment.