From b3fe4b47bc0f14dcd5300dfbd2ba855d6a0a1ee3 Mon Sep 17 00:00:00 2001 From: Eric Olkowski Date: Tue, 6 Sep 2022 10:30:13 -0400 Subject: [PATCH 1/2] feat(resizeObserver): allow opting out of using requestAnimationFrame --- .../react-core/src/helpers/resizeObserver.tsx | 74 +++++++++++++++++-- 1 file changed, 69 insertions(+), 5 deletions(-) diff --git a/packages/react-core/src/helpers/resizeObserver.tsx b/packages/react-core/src/helpers/resizeObserver.tsx index 1f5a384a3df..3bad1955954 100644 --- a/packages/react-core/src/helpers/resizeObserver.tsx +++ b/packages/react-core/src/helpers/resizeObserver.tsx @@ -4,7 +4,7 @@ import { canUseDOM } from './util'; * This function creates a ResizeObserver used to handle resize events for the given containerRef. If ResizeObserver * or the given containerRef are not available, a window resize event listener is used by default. * - * Example 1: + * Example 1a - Without debounced method passed in: * * private containerRef = React.createRef(); * private observer: any = () => {}; @@ -31,7 +31,34 @@ import { canUseDOM } from './util'; * ); * } * - * Example 2: + * Example 1b - With debounced method passed in: + * + * private containerRef = React.createRef(); + * private observer: any = () => {}; + * + * public componentDidMount() { + * this.observer = getResizeObserver(this.containerRef.current, debounce(this.handleResize, 250), false); + * } + * + * public componentWillUnmount() { + * this.observer(); + * } + * + * private handleResize = () => { + * if (this.containerRef.current && this.containerRef.current.clientWidth) { + * this.setState({ width: this.containerRef.current.clientWidth }); + * } + * }; + * + * public render() { + * return ( + *
+ * + *
+ * ); + * } + * + * Example 2a - Without debounced method passed in: * * private inputRef = React.createRef(); * private observer: any = () => {}; @@ -56,11 +83,41 @@ import { canUseDOM } from './util'; * ); * } * + * Example 2b - With debounced method passed in: + * + * private inputRef = React.createRef(); + * private observer: any = () => {}; + * + * public componentDidMount() { + * this.observer = getResizeObserver(this.inputRef.current, debounce(this.handleResize, 250), false); + * } + * + * public componentWillUnmount() { + * this.observer(); + * } + * + * private handleResize = () => { + * if (this.inputRef.current) { + * trimLeft(inputRef.current, String(this.props.value)); + * } + * }; + * + * public render() { + * return ( + * + * ); + * } + * * @param {Element} containerRefElement The container reference to observe * @param {Function} handleResize The function to call for resize events + * @param {boolean} useRequestAnimationFrame Whether to pass the handleResize function as a callback to requestAnimationFrame. Pass in false when the function passed in is debounced. Defaults to true. * @return {Function} The function used to unobserve resize events */ -export const getResizeObserver = (containerRefElement: Element, handleResize: () => void) => { +export const getResizeObserver = ( + containerRefElement: Element, + handleResize: () => void, + useRequestAnimationFrame: boolean = true +) => { let unobserve: any; if (canUseDOM) { @@ -69,11 +126,18 @@ export const getResizeObserver = (containerRefElement: Element, handleResize: () if (containerRefElement && ResizeObserver) { const resizeObserver = new ResizeObserver((entries: any) => { // Wrap resize function in requestAnimationFrame to avoid "ResizeObserver loop limit exceeded" errors - window.requestAnimationFrame(() => { + if (useRequestAnimationFrame) { + window.requestAnimationFrame(() => { + if (Array.isArray(entries) && entries.length > 0) { + handleResize(); + } + }); + // Avoid wrapping function in requestAnimationFrame if the function is debounced + } else { if (Array.isArray(entries) && entries.length > 0) { handleResize(); } - }); + } }); resizeObserver.observe(containerRefElement); unobserve = () => resizeObserver.unobserve(containerRefElement); From 0548e65dc38568f200e07e7d390754762ba518d4 Mon Sep 17 00:00:00 2001 From: Eric Olkowski Date: Tue, 6 Sep 2022 14:29:37 -0400 Subject: [PATCH 2/2] Update examples --- .../react-core/src/helpers/resizeObserver.tsx | 52 ++----------------- 1 file changed, 3 insertions(+), 49 deletions(-) diff --git a/packages/react-core/src/helpers/resizeObserver.tsx b/packages/react-core/src/helpers/resizeObserver.tsx index 3bad1955954..7a093ab4c71 100644 --- a/packages/react-core/src/helpers/resizeObserver.tsx +++ b/packages/react-core/src/helpers/resizeObserver.tsx @@ -4,7 +4,7 @@ import { canUseDOM } from './util'; * This function creates a ResizeObserver used to handle resize events for the given containerRef. If ResizeObserver * or the given containerRef are not available, a window resize event listener is used by default. * - * Example 1a - Without debounced method passed in: + * Example 1: * * private containerRef = React.createRef(); * private observer: any = () => {}; @@ -31,34 +31,7 @@ import { canUseDOM } from './util'; * ); * } * - * Example 1b - With debounced method passed in: - * - * private containerRef = React.createRef(); - * private observer: any = () => {}; - * - * public componentDidMount() { - * this.observer = getResizeObserver(this.containerRef.current, debounce(this.handleResize, 250), false); - * } - * - * public componentWillUnmount() { - * this.observer(); - * } - * - * private handleResize = () => { - * if (this.containerRef.current && this.containerRef.current.clientWidth) { - * this.setState({ width: this.containerRef.current.clientWidth }); - * } - * }; - * - * public render() { - * return ( - *
- * - *
- * ); - * } - * - * Example 2a - Without debounced method passed in: + * Example 2: * * private inputRef = React.createRef(); * private observer: any = () => {}; @@ -83,31 +56,12 @@ import { canUseDOM } from './util'; * ); * } * - * Example 2b - With debounced method passed in: - * - * private inputRef = React.createRef(); - * private observer: any = () => {}; + * Example 3 - With debounced method passed in: * * public componentDidMount() { * this.observer = getResizeObserver(this.inputRef.current, debounce(this.handleResize, 250), false); * } * - * public componentWillUnmount() { - * this.observer(); - * } - * - * private handleResize = () => { - * if (this.inputRef.current) { - * trimLeft(inputRef.current, String(this.props.value)); - * } - * }; - * - * public render() { - * return ( - * - * ); - * } - * * @param {Element} containerRefElement The container reference to observe * @param {Function} handleResize The function to call for resize events * @param {boolean} useRequestAnimationFrame Whether to pass the handleResize function as a callback to requestAnimationFrame. Pass in false when the function passed in is debounced. Defaults to true.