Skip to content
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Use internal state to force re-renders when the anchor ref changes
  • Loading branch information
ciampo committed Sep 2, 2022
commit 47bc39f7eb47bc83c6ddc697be8ee066ba272862
11 changes: 8 additions & 3 deletions packages/components/src/tooltip/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
concatChildren,
useEffect,
useState,
useRef,
useCallback,
} from '@wordpress/element';
import { useDebounce, useMergeRefs } from '@wordpress/compose';

Expand Down Expand Up @@ -124,11 +124,16 @@ function Tooltip( props ) {
const [ isMouseDown, setIsMouseDown ] = useState( false );
const [ isOver, setIsOver ] = useState( false );
const delayedSetIsOver = useDebounce( setIsOver, delay );
// Using internal state (instead of a ref) for the popover anchor to make sure
// that the component re-renders when the anchor updates.
const [ popoverAnchor, setPopoverAnchor ] = useState();

// Create a reference to the Tooltip's child, to be passed to the Popover
// so that the Tooltip can be correctly positioned. Also, merge with the
// existing ref for the first child, so that its ref is preserved.
const childRef = useRef( null );
const childRef = useCallback( ( node ) => {
setPopoverAnchor( node ?? undefined );
}, [] );
const existingChildRef = Children.toArray( children )[ 0 ]?.ref;
const mergedChildRefs = useMergeRefs( [ childRef, existingChildRef ] );

Expand Down Expand Up @@ -253,7 +258,7 @@ function Tooltip( props ) {
: getRegularElement;

const popoverData = {
anchor: childRef.current,
anchor: popoverAnchor,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the difference between components where we can simply pass childRef.current here, versus components that need a useState layer like this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good question. I think that in theory all components using Popover should make sure that, if they internally use a ref to grab an anchor to pass to Popover, they re-render when the ref value updates.

We should probably assess that this is true in #43691, once we have all sub-PRs merged. I've added a todo item in the PR's description

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. If this pattern is going to come up a lot, could we perhaps simplify it a little by removing the useCallback layer? So const childRef = setPopoverAnchor; (line 134) and then

Suggested change
anchor: popoverAnchor,
anchor: popoverAnchor ?? undefined,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good shout! Done in 16d345e

isOver,
offset: 4,
position,
Expand Down