@atlaskit/renderer
Version:
Renderer component
88 lines (82 loc) • 2.98 kB
JavaScript
import { useRef, useCallback } from 'react';
/**
* Hook that provides functionality to wait for layout stability before performing an action.
* Uses ResizeObserver to detect when a container has stopped resizing (e.g., images finished loading).
*/
export const useStableScroll = (options = {}) => {
const {
stabilityWaitTime = 200,
maxStabilityWaitTime = 10_000
} = options;
const stabilityTimeoutRef = useRef(null);
const resizeObserverRef = useRef(null);
const lastStableTimeRef = useRef(0);
const onStableCallbackRef = useRef(null);
const cleanup = useCallback(() => {
if (stabilityTimeoutRef.current) {
clearTimeout(stabilityTimeoutRef.current);
stabilityTimeoutRef.current = null;
}
if (resizeObserverRef.current) {
resizeObserverRef.current.disconnect();
resizeObserverRef.current = null;
}
onStableCallbackRef.current = null;
lastStableTimeRef.current = 0;
}, []);
const scheduleStabilityCheck = useCallback(() => {
// Clear any existing stability timeout.
if (stabilityTimeoutRef.current) {
clearTimeout(stabilityTimeoutRef.current);
stabilityTimeoutRef.current = null;
}
// Check if we've exceeded the maximum stability wait time.
const now = Date.now();
if (lastStableTimeRef.current === 0) {
lastStableTimeRef.current = now;
} else if (now - lastStableTimeRef.current > maxStabilityWaitTime) {
// We've waited too long for stability, call the callback now.
if (onStableCallbackRef.current) {
onStableCallbackRef.current();
cleanup();
}
return;
}
// Set a timeout to call the callback after the stability wait time.
stabilityTimeoutRef.current = setTimeout(() => {
if (onStableCallbackRef.current) {
onStableCallbackRef.current();
cleanup();
}
}, stabilityWaitTime);
}, [stabilityWaitTime, maxStabilityWaitTime, cleanup]);
const waitForStability = useCallback((container, onStable) => {
// Clean up any existing observer
cleanup();
// Store the callback
onStableCallbackRef.current = onStable;
// Check if ResizeObserver is available
if (typeof ResizeObserver === 'undefined') {
// Fallback: just call the callback after the stability wait time
stabilityTimeoutRef.current = setTimeout(() => {
if (onStableCallbackRef.current) {
onStableCallbackRef.current();
cleanup();
}
}, stabilityWaitTime);
return;
}
// Create a ResizeObserver to monitor the container for size changes.
resizeObserverRef.current = new ResizeObserver(() => {
// Container size changed, reset stability timer.
scheduleStabilityCheck();
});
resizeObserverRef.current.observe(container);
// Start the initial stability check
scheduleStabilityCheck();
}, [stabilityWaitTime, scheduleStabilityCheck, cleanup]);
return {
waitForStability,
cleanup
};
};