-
Notifications
You must be signed in to change notification settings - Fork 4.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
…64943) * Simplify useResizeObserver * Loop through all resize entries * Add `useEvent` util. * Add `useObserveElementSize` util. * Simplify `useResizeObserver` by using `useEvent` and `useObserveElementSize`. * Switch to layout effect and accept refs too. * Prevent initial re-render in ResizeElement. * Better error message. * Improved example of useEvent. * Update packages/compose/src/hooks/use-event/index.ts Co-authored-by: Marin Atanasov <[email protected]> * Sync docs. * Avoid redundant resize listener calls. * Switch to structural check. * Improve example. * Fix docs. * Make `useObserveElementSize` generic. * New API that returns a ref. * Make utility private for now. * Mark legacy `useResizeObserver` as such. * Rename `useObserveElementSize` to `useResizeObserver`. * Add return type. * Add signature as overload. * Add support for legacy API. * Move into subdirectory. * Minor import fix. * Fix docgen to support overloads (will pick up the first function signature). * Replace legacy utility with the new one. * Apply feedback. * Clean up and document. * Added changelog entries. --------- Co-authored-by: jsnajdr <[email protected]> Co-authored-by: DaniGuardiola <[email protected]> Co-authored-by: tyxla <[email protected]> Co-authored-by: ciampo <[email protected]> Co-authored-by: youknowriad <[email protected]>
- Loading branch information
1 parent
312fa6e
commit 537fb18
Showing
9 changed files
with
238 additions
and
44 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useRef, useInsertionEffect, useCallback } from '@wordpress/element'; | ||
|
||
/** | ||
* Any function. | ||
*/ | ||
export type AnyFunction = ( ...args: any ) => any; | ||
|
||
/** | ||
* Creates a stable callback function that has access to the latest state and | ||
* can be used within event handlers and effect callbacks. Throws when used in | ||
* the render phase. | ||
* | ||
* @param callback The callback function to wrap. | ||
* | ||
* @example | ||
* | ||
* ```tsx | ||
* function Component( props ) { | ||
* const onClick = useEvent( props.onClick ); | ||
* useEffect( () => { | ||
* onClick(); | ||
* // Won't trigger the effect again when props.onClick is updated. | ||
* }, [ onClick ] ); | ||
* // Won't re-render Button when props.onClick is updated (if `Button` is | ||
* // wrapped in `React.memo`). | ||
* return <Button onClick={ onClick } />; | ||
* } | ||
* ``` | ||
*/ | ||
export default function useEvent< T extends AnyFunction >( | ||
/** | ||
* The callback function to wrap. | ||
*/ | ||
callback?: T | ||
) { | ||
const ref = useRef< AnyFunction | undefined >( () => { | ||
throw new Error( | ||
'Callbacks created with `useEvent` cannot be called during rendering.' | ||
); | ||
} ); | ||
useInsertionEffect( () => { | ||
ref.current = callback; | ||
} ); | ||
return useCallback< AnyFunction >( | ||
( ...args ) => ref.current?.( ...args ), | ||
[] | ||
) as T; | ||
} |
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
119 changes: 119 additions & 0 deletions
119
packages/compose/src/hooks/use-resize-observer/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { useRef } from '@wordpress/element'; | ||
/** | ||
* Internal dependencies | ||
*/ | ||
import useEvent from '../use-event'; | ||
import type { ObservedSize } from './_legacy'; | ||
import _useLegacyResizeObserver from './_legacy'; | ||
/** | ||
* External dependencies | ||
*/ | ||
import type { ReactElement } from 'react'; | ||
|
||
// This is the current implementation of `useResizeObserver`. | ||
// | ||
// The legacy implementation is still supported for backwards compatibility. | ||
// This is achieved by overloading the exported function with both signatures, | ||
// and detecting which API is being used at runtime. | ||
function _useResizeObserver< T extends HTMLElement >( | ||
callback: ResizeObserverCallback, | ||
resizeObserverOptions: ResizeObserverOptions = {} | ||
): ( element?: T | null ) => void { | ||
const callbackEvent = useEvent( callback ); | ||
|
||
const observedElementRef = useRef< T | null >(); | ||
const resizeObserverRef = useRef< ResizeObserver >(); | ||
return useEvent( ( element?: T | null ) => { | ||
if ( element === observedElementRef.current ) { | ||
return; | ||
} | ||
observedElementRef.current = element; | ||
|
||
// Set up `ResizeObserver`. | ||
resizeObserverRef.current ??= new ResizeObserver( callbackEvent ); | ||
const { current: resizeObserver } = resizeObserverRef; | ||
|
||
// Unobserve previous element. | ||
if ( observedElementRef.current ) { | ||
resizeObserver.unobserve( observedElementRef.current ); | ||
} | ||
|
||
// Observe new element. | ||
if ( element ) { | ||
resizeObserver.observe( element, resizeObserverOptions ); | ||
} | ||
} ); | ||
} | ||
|
||
/** | ||
* Sets up a [`ResizeObserver`](https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API) | ||
* for an HTML or SVG element. | ||
* | ||
* Pass the returned setter as a callback ref to the React element you want | ||
* to observe, or use it in layout effects for advanced use cases. | ||
* | ||
* @example | ||
* | ||
* ```tsx | ||
* const setElement = useResizeObserver( | ||
* ( resizeObserverEntries ) => console.log( resizeObserverEntries ), | ||
* { box: 'border-box' } | ||
* ); | ||
* <div ref={ setElement } />; | ||
* | ||
* // The setter can be used in other ways, for example: | ||
* useLayoutEffect( () => { | ||
* setElement( document.querySelector( `data-element-id="${ elementId }"` ) ); | ||
* }, [ elementId ] ); | ||
* ``` | ||
* | ||
* @param callback The `ResizeObserver` callback - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver#callback). | ||
* @param options Options passed to `ResizeObserver.observe` when called - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe#options). Changes will be ignored. | ||
*/ | ||
export default function useResizeObserver< T extends Element >( | ||
/** | ||
* The `ResizeObserver` callback - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/ResizeObserver#callback). | ||
*/ | ||
callback: ResizeObserverCallback, | ||
/** | ||
* Options passed to `ResizeObserver.observe` when called - [MDN docs](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver/observe#options). Changes will be ignored. | ||
*/ | ||
options?: ResizeObserverOptions | ||
): ( element?: T | null ) => void; | ||
|
||
/** | ||
* **This is a legacy API and should not be used.** | ||
* | ||
* @deprecated Use the other `useResizeObserver` API instead: `const ref = useResizeObserver( ( entries ) => { ... } )`. | ||
* | ||
* Hook which allows to listen to the resize event of any target element when it changes size. | ||
* _Note: `useResizeObserver` will report `null` sizes until after first render. | ||
* | ||
* @example | ||
* | ||
* ```js | ||
* const App = () => { | ||
* const [ resizeListener, sizes ] = useResizeObserver(); | ||
* | ||
* return ( | ||
* <div> | ||
* { resizeListener } | ||
* Your content here | ||
* </div> | ||
* ); | ||
* }; | ||
* ``` | ||
*/ | ||
export default function useResizeObserver(): [ ReactElement, ObservedSize ]; | ||
|
||
export default function useResizeObserver< T extends HTMLElement >( | ||
callback?: ResizeObserverCallback, | ||
options: ResizeObserverOptions = {} | ||
): ( ( element?: T | null ) => void ) | [ ReactElement, ObservedSize ] { | ||
return callback | ||
? _useResizeObserver( callback, options ) | ||
: _useLegacyResizeObserver(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters