diff --git a/packages/components/package.json b/packages/components/package.json
index 459884fe35bd45..c5192e41d0b65f 100644
--- a/packages/components/package.json
+++ b/packages/components/package.json
@@ -52,7 +52,6 @@
"moment": "^2.22.1",
"re-resizable": "^6.0.0",
"react-dates": "^17.1.1",
- "react-resize-aware": "^3.0.0",
"react-spring": "^8.0.20",
"reakit": "^1.0.0-beta.12",
"rememo": "^3.0.0",
diff --git a/packages/components/src/placeholder/index.js b/packages/components/src/placeholder/index.js
index 251690a0a8bb7c..d37b3566f24300 100644
--- a/packages/components/src/placeholder/index.js
+++ b/packages/components/src/placeholder/index.js
@@ -2,7 +2,11 @@
* External dependencies
*/
import classnames from 'classnames';
-import useResizeAware from 'react-resize-aware';
+
+/**
+ * WordPress dependencies
+ */
+import { useResizeObserver } from '@wordpress/compose';
/**
* Internal dependencies
@@ -26,9 +30,9 @@ function Placeholder( {
isColumnLayout,
...additionalProps
} ) {
- const [ resizeListener, { width } ] = useResizeAware();
+ const [ resizeListener, { width } ] = useResizeObserver();
- // Since `useResizeAware` will report a width of `null` until after the
+ // Since `useResizeObserver` will report a width of `null` until after the
// first render, avoid applying any modifier classes until width is known.
let modifierClassNames;
if ( typeof width === 'number' ) {
diff --git a/packages/components/src/placeholder/test/index.js b/packages/components/src/placeholder/test/index.js
index cf14e596469c9c..a0c50d8a1bf96f 100644
--- a/packages/components/src/placeholder/test/index.js
+++ b/packages/components/src/placeholder/test/index.js
@@ -2,23 +2,24 @@
* External dependencies
*/
import { shallow } from 'enzyme';
-import useResizeAware from 'react-resize-aware';
/**
* WordPress dependencies
*/
import { more } from '@wordpress/icons';
+import { useResizeObserver } from '@wordpress/compose';
/**
* Internal dependencies
*/
import Placeholder from '../';
-jest.mock( 'react-resize-aware' );
-
describe( 'Placeholder', () => {
beforeEach( () => {
- useResizeAware.mockReturnValue( [
, { width: 320 } ] );
+ useResizeObserver.mockReturnValue( [
+
,
+ { width: 320 },
+ ] );
} );
describe( 'basic rendering', () => {
@@ -109,8 +110,8 @@ describe( 'Placeholder', () => {
} );
describe( 'resize aware', () => {
- it( 'should not assign modifier class in first-pass `null` width from `useResizeAware`', () => {
- useResizeAware.mockReturnValue( [
+ it( 'should not assign modifier class in first-pass `null` width from `useResizeObserver`', () => {
+ useResizeObserver.mockReturnValue( [
,
{ width: 320 },
] );
@@ -123,7 +124,7 @@ describe( 'Placeholder', () => {
} );
it( 'should assign modifier class', () => {
- useResizeAware.mockReturnValue( [
+ useResizeObserver.mockReturnValue( [
,
{ width: null },
] );
diff --git a/packages/compose/README.md b/packages/compose/README.md
index b473af456e8eca..bfc6d2b0247590 100644
--- a/packages/compose/README.md
+++ b/packages/compose/README.md
@@ -157,6 +157,30 @@ _Returns_
- `boolean`: Reduced motion preference value.
+
# **useResizeObserver**
+
+Hook which allows to listen the resize event of any target element when it changes sizes.
+_Note: `useResizeObserver` will report `null` until after first render_
+
+_Usage_
+
+```js
+const App = () => {
+ const [ resizeListener, sizes ] = useResizeObserver();
+
+ return (
+
+ { resizeListener }
+ Your content here
+
+ );
+};
+```
+
+_Returns_
+
+- `Array`: An array of {Element} `resizeListener` and {?Object} `sizes` with properties `width` and `height`
+
# **useViewportMatch**
Returns true if the viewport matches the given query, or false otherwise.
diff --git a/packages/compose/package.json b/packages/compose/package.json
index 8f677e9c875209..98b445f213cb72 100644
--- a/packages/compose/package.json
+++ b/packages/compose/package.json
@@ -27,7 +27,8 @@
"@wordpress/element": "file:../element",
"@wordpress/is-shallow-equal": "file:../is-shallow-equal",
"lodash": "^4.17.15",
- "mousetrap": "^1.6.2"
+ "mousetrap": "^1.6.2",
+ "react-resize-aware": "^3.0.0"
},
"publishConfig": {
"access": "public"
diff --git a/packages/compose/src/hooks/use-resize-observer/index.js b/packages/compose/src/hooks/use-resize-observer/index.js
new file mode 100644
index 00000000000000..a46539e10438db
--- /dev/null
+++ b/packages/compose/src/hooks/use-resize-observer/index.js
@@ -0,0 +1,28 @@
+/**
+ * External dependencies
+ */
+import useResizeAware from 'react-resize-aware';
+
+/**
+ * Hook which allows to listen the resize event of any target element when it changes sizes.
+ * _Note: `useResizeObserver` will report `null` until after first render_
+ *
+ * @return {Array} An array of {Element} `resizeListener` and {?Object} `sizes` with properties `width` and `height`
+ *
+ * @example
+ *
+ * ```js
+ * const App = () => {
+ * const [ resizeListener, sizes ] = useResizeObserver();
+ *
+ * return (
+ *
+ * { resizeListener }
+ * Your content here
+ *
+ * );
+ * };
+ * ```
+ *
+ */
+export default useResizeAware;
diff --git a/packages/compose/src/hooks/use-resize-observer/index.native.js b/packages/compose/src/hooks/use-resize-observer/index.native.js
new file mode 100644
index 00000000000000..26e6f647f5b5a7
--- /dev/null
+++ b/packages/compose/src/hooks/use-resize-observer/index.native.js
@@ -0,0 +1,55 @@
+/**
+ * External dependencies
+ */
+import { View, StyleSheet } from 'react-native';
+/**
+ * WordPress dependencies
+ */
+import { useState, useCallback } from '@wordpress/element';
+
+/**
+ * Hook which allows to listen the resize event of any target element when it changes sizes.
+ *
+ * @return {Array} An array of {Element} `resizeListener` and {?Object} `sizes` with properties `width` and `height`
+ *
+ * @example
+ *
+ * ```js
+ * const App = () => {
+ * const [ resizeListener, sizes ] = useResizeObserver();
+ *
+ * return (
+ *
+ * { resizeListener }
+ * Your content here
+ *
+ * );
+ * };
+ * ```
+ *
+ */
+const useResizeObserver = () => {
+ const [ measurements, setMeasurements ] = useState( null );
+
+ const onLayout = useCallback( ( { nativeEvent } ) => {
+ const { width, height } = nativeEvent.layout;
+ setMeasurements( ( prevState ) => {
+ if (
+ ! prevState ||
+ prevState.width !== width ||
+ prevState.height !== height
+ ) {
+ return { width, height };
+ }
+ return prevState;
+ } );
+ }, [] );
+
+ const observer = (
+
+ );
+
+ return [ observer, measurements ];
+};
+
+export default useResizeObserver;
diff --git a/packages/compose/src/hooks/use-resize-observer/test/index.native.js b/packages/compose/src/hooks/use-resize-observer/test/index.native.js
new file mode 100644
index 00000000000000..4e4165cd6a4db1
--- /dev/null
+++ b/packages/compose/src/hooks/use-resize-observer/test/index.native.js
@@ -0,0 +1,50 @@
+/**
+ * External dependencies
+ */
+import { create, act } from 'react-test-renderer';
+import { View } from 'react-native';
+
+/**
+ * Internal dependencies
+ */
+import useResizeObserver from '../';
+
+const TestComponent = ( { onLayout } ) => {
+ const [ resizeObserver, sizes ] = useResizeObserver();
+
+ return (
+
+ { resizeObserver }
+
+ );
+};
+
+const renderWithOnLayout = ( component ) => {
+ const testComponent = create( component );
+
+ const mockNativeEvent = {
+ nativeEvent: {
+ layout: {
+ width: 300,
+ height: 500,
+ },
+ },
+ };
+
+ act( () => {
+ testComponent.toJSON().children[ 0 ].props.onLayout( mockNativeEvent );
+ } );
+
+ return testComponent.toJSON();
+};
+
+describe( 'useResizeObserver()', () => {
+ it( 'should return "{ width: 300, height: 500 }"', () => {
+ const component = renderWithOnLayout(
);
+
+ expect( component.props.sizes ).toMatchObject( {
+ width: 300,
+ height: 500,
+ } );
+ } );
+} );
diff --git a/packages/compose/src/index.js b/packages/compose/src/index.js
index 3a1aabde92cfef..083d41c3f92693 100644
--- a/packages/compose/src/index.js
+++ b/packages/compose/src/index.js
@@ -19,3 +19,4 @@ export { default as useKeyboardShortcut } from './hooks/use-keyboard-shortcut';
export { default as useMediaQuery } from './hooks/use-media-query';
export { default as useReducedMotion } from './hooks/use-reduced-motion';
export { default as useViewportMatch } from './hooks/use-viewport-match';
+export { default as useResizeObserver } from './hooks/use-resize-observer';
diff --git a/packages/compose/src/index.native.js b/packages/compose/src/index.native.js
index 6c1e89a35f8791..b42258930b6ce4 100644
--- a/packages/compose/src/index.native.js
+++ b/packages/compose/src/index.native.js
@@ -6,3 +6,4 @@ export { default as withPreferredColorScheme } from './higher-order/with-preferr
// Hooks
export { default as usePreferredColorScheme } from './hooks/use-preferred-color-scheme';
+export { default as useResizeObserver } from './hooks/use-resize-observer';
diff --git a/storybook/test/__snapshots__/index.js.snap b/storybook/test/__snapshots__/index.js.snap
index b9f1491f1eb4f6..2b0d00fffcf0bc 100644
--- a/storybook/test/__snapshots__/index.js.snap
+++ b/storybook/test/__snapshots__/index.js.snap
@@ -4311,29 +4311,8 @@ exports[`Storyshots Components/Panel With Icon 1`] = `
exports[`Storyshots Components/Placeholder Default 1`] = `
-
diff --git a/test/unit/config/global-mocks.js b/test/unit/config/global-mocks.js
index 60b430759ab287..0398af83c3a0b3 100644
--- a/test/unit/config/global-mocks.js
+++ b/test/unit/config/global-mocks.js
@@ -1,6 +1,11 @@
jest.mock( '@wordpress/compose', () => {
+ const App = () => null;
return {
...jest.requireActual( '@wordpress/compose' ),
useViewportMatch: jest.fn(),
+ useResizeObserver: jest.fn( () => [
+
,
+ { width: 700, height: 500 },
+ ] ),
};
} );