Skip to content

Instantly share code, notes, and snippets.

@souporserious
Created November 22, 2019 20:25
Show Gist options
  • Save souporserious/76a0655cdd148eeddfdfb337f5d4fba7 to your computer and use it in GitHub Desktop.
Save souporserious/76a0655cdd148eeddfdfb337f5d4fba7 to your computer and use it in GitHub Desktop.
react-measure v4 iteration
export function useMeasure(
measureRef,
getDimensions = getRect,
shouldUpdate = (prev, next) => !shallowEqual(prev, next)
) {
const [dimensions, setDimensions] = useState(
getDimensions(measureRef.current)
)
const mutationObserver = useRef(null)
const resizeObserver = useRef(null)
const animationId = useRef(null)
const measure = () => {
if (animationId.current === null) {
animationId.current = window.requestAnimationFrame(() => {
setDimensions(dimensions => {
const nextDimensions = getDimensions(measureRef.current)
if (shouldUpdate(dimensions, nextDimensions)) {
return nextDimensions
} else {
return dimensions
}
})
animationId.current = null
})
}
}
if (typeof window !== 'undefined') {
if (!mutationObserver.current) {
mutationObserver.current = new MutationObserver(measure)
}
if (!resizeObserver.current) {
resizeObserver.current = new ResizeObserver(measure)
}
useLayoutEffect(() => {
setDimensions(getDimensions(measureRef.current))
}, [])
}
useEffect(() => {
if (mutationObserver.current) {
mutationObserver.current.observe(measureRef.current, {
characterData: true,
childList: true,
subtree: true,
})
}
if (resizeObserver.current) {
resizeObserver.current.observe(measureRef.current)
}
return () => {
if (mutationObserver.current) {
mutationObserver.current.disconnect()
}
if (resizeObserver.current) {
resizeObserver.current.disconnect()
}
if (animationId.current) {
window.cancelAnimationFrame(animationId.current)
}
}
}, [])
return dimensions
}
@bmcmahen
Copy link

bmcmahen commented Nov 22, 2019

Looks awesome. I often find it necessary to return a callback ref for components like these where you can't determine if the ref is present upon mount. I.e,:

function useMeasure () {
  const [ref, setRef] = useState()
  const setRefFn = useCallback // etc. 
  return { ref: setRefFn, dimensions }
}

It's not as elegant as accepting a ref as an argument to the hook though. But I don't see much of an alternative.

@souporserious
Copy link
Author

souporserious commented Nov 22, 2019

@bmcmahen ah yes! I plan on exposing the measure function for this reason.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment