Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New useBlockElementRef hook for storing block element into a ref #63799

Merged
merged 3 commits into from
Jul 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ import { Button } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { chevronRightSmall, Icon } from '@wordpress/icons';
import { useRef } from '@wordpress/element';

/**
* Internal dependencies
*/
import BlockTitle from '../block-title';
import { store as blockEditorStore } from '../../store';
import { unlock } from '../../lock-unlock';
import { __unstableUseBlockRef as useBlockRef } from '../block-list/use-block-props/use-block-refs';
import { useBlockElementRef } from '../block-list/use-block-props/use-block-refs';
import getEditorRegion from '../../utils/get-editor-region';

/**
Expand Down Expand Up @@ -41,7 +42,8 @@ function BlockBreadcrumb( { rootLabelText } ) {

// We don't care about this specific ref, but this is a way
// to get a ref within the editor canvas so we can focus it later.
const blockRef = useBlockRef( clientId );
const blockRef = useRef();
useBlockElementRef( clientId, blockRef );

/*
* Disable reason: The `list` ARIA role is redundant but
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { throttle } from '@wordpress/compose';
import BlockDraggableChip from './draggable-chip';
import useScrollWhenDragging from './use-scroll-when-dragging';
import { store as blockEditorStore } from '../../store';
import { __unstableUseBlockRef as useBlockRef } from '../block-list/use-block-props/use-block-refs';
import { useBlockElement } from '../block-list/use-block-props/use-block-refs';
import { isDropTargetValid } from '../use-block-drop-zone';

const BlockDraggable = ( {
Expand Down Expand Up @@ -82,8 +82,8 @@ const BlockDraggable = ( {
}, [] );

// Find the root of the editor iframe.
const blockRef = useBlockRef( clientIds[ 0 ] );
const editorRoot = blockRef.current?.closest( 'body' );
const blockEl = useBlockElement( clientIds[ 0 ] );
const editorRoot = blockEl?.closest( 'body' );

/*
* Add a dragover event listener to the editor root to track the blocks being dragged over.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
/**
* WordPress dependencies
*/
import {
useContext,
useMemo,
useRef,
useState,
useLayoutEffect,
} from '@wordpress/element';
import { useContext, useState, useLayoutEffect } from '@wordpress/element';
import { useRefEffect } from '@wordpress/compose';

/**
Expand All @@ -16,7 +10,7 @@ import { useRefEffect } from '@wordpress/compose';
import { BlockRefs } from '../../provider/block-refs-provider';

/** @typedef {import('@wordpress/element').RefCallback} RefCallback */
/** @typedef {import('@wordpress/element').RefObject} RefObject */
/** @typedef {import('@wordpress/element').Ref} Ref */

/**
* Provides a ref to the BlockRefs context.
Expand All @@ -36,31 +30,33 @@ export function useBlockRefProvider( clientId ) {
);
}

function assignRef( ref, value ) {
if ( typeof ref === 'function' ) {
ref( value );
} else if ( ref ) {
ref.current = value;
}
}

/**
* Gets a ref pointing to the current block element. Continues to return the same
* stable ref object even if the `clientId` argument changes. This hook is not
* reactive, i.e., it won't trigger a rerender of the calling component if the
* ref value changes. For reactive use cases there is the `useBlockElement` hook.
*
* @param {string} clientId The client ID to get a ref for.
* Tracks the DOM element for the block identified by `clientId` and assigns it to the `ref`
* whenever it changes.
*
* @return {RefObject} A ref containing the element.
* @param {string} clientId The client ID to track.
* @param {Ref} ref The ref object/callback to assign to.
*/
function useBlockRef( clientId ) {
export function useBlockElementRef( clientId, ref ) {
Copy link
Member

Choose a reason for hiding this comment

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

Why can't we set the ref that is provided here directly on refsMap to be used by the block?

Copy link
Member Author

Choose a reason for hiding this comment

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

You mean, the refs would be stored directly in the refsMap instead of the element? And the refsMap job would be to "forward" the ref callback calls to the registered consumers?

I think in that case it would be hard to register a new ref at a time when the block is already mounted. When calling:

useBlockElementRef( clientId, ref );

The ref wouldn't be set until the block element is changed or recreated.

const { refsMap } = useContext( BlockRefs );
const latestClientId = useRef();
latestClientId.current = clientId;

// Always return an object, even if no ref exists for a given client ID, so
// that `current` works at a later point.
return useMemo(
() => ( {
get current() {
return refsMap.get( latestClientId.current ) ?? null;
},
} ),
[ refsMap ]
);
useLayoutEffect( () => {
assignRef( ref, refsMap.get( clientId ) );
const unsubscribe = refsMap.subscribe( clientId, () =>
assignRef( ref, refsMap.get( clientId ) )
);
return () => {
unsubscribe();
assignRef( ref, null );
};
}, [ refsMap, clientId, ref ] );
}

/**
Expand All @@ -71,20 +67,8 @@ function useBlockRef( clientId ) {
*
* @return {Element|null} The block's wrapper element.
*/
function useBlockElement( clientId ) {
const { refsMap } = useContext( BlockRefs );
export function useBlockElement( clientId ) {
const [ blockElement, setBlockElement ] = useState( null );
// Delay setting the resulting `blockElement` until an effect. If the block element
// changes (i.e., the block is unmounted and re-mounted), this allows enough time
// for the ref callbacks to clean up the old element and set the new one.
useLayoutEffect( () => {
setBlockElement( refsMap.get( clientId ) );
return refsMap.subscribe( clientId, () =>
setBlockElement( refsMap.get( clientId ) )
);
}, [ refsMap, clientId ] );
useBlockElementRef( clientId, setBlockElement );
return blockElement;
}

export { useBlockRef as __unstableUseBlockRef };
export { useBlockElement as __unstableUseBlockElement };
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { useEffect, useState, useMemo, forwardRef } from '@wordpress/element';
/**
* Internal dependencies
*/
import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs';
import { useBlockElement } from '../block-list/use-block-props/use-block-refs';
import { PrivateBlockPopover } from '.';

function BlockPopoverCover(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { isRTL } from '@wordpress/i18n';
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs';
import { useBlockElement } from '../block-list/use-block-props/use-block-refs';
import usePopoverScroll from './use-popover-scroll';

const MAX_POPOVER_RECOMPUTE_COUNTER = Number.MAX_SAFE_INTEGER;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import {
/**
* Internal dependencies
*/
import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs';
import { useBlockElement } from '../block-list/use-block-props/use-block-refs';
import usePopoverScroll from './use-popover-scroll';

const MAX_POPOVER_RECOMPUTE_COUNTER = Number.MAX_SAFE_INTEGER;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import BlockTitle from '../block-title';
import BlockIcon from '../block-icon';
import { store as blockEditorStore } from '../../store';
import BlockDraggable from '../block-draggable';
import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs';
import { useBlockElement } from '../block-list/use-block-props/use-block-refs';

/**
* Block selection button component, displaying the label of the block. If the block
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs';
import { useBlockElement } from '../block-list/use-block-props/use-block-refs';
import { hasStickyOrFixedPositionValue } from '../../hooks/position';

const COMMON_PROPS = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useState, useEffect } from '@wordpress/element';
/**
* Internal dependencies
*/
import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs';
import { useBlockElement } from '../block-list/use-block-props/use-block-refs';
import BlockPopoverCover from '../block-popover/cover';
import { getComputedCSS, getGridTracks, getClosestTrack } from './utils';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { __experimentalUseDropZone as useDropZone } from '@wordpress/compose';
/**
* Internal dependencies
*/
import { __unstableUseBlockElement as useBlockElement } from '../block-list/use-block-props/use-block-refs';
import { useBlockElement } from '../block-list/use-block-props/use-block-refs';
import BlockPopoverCover from '../block-popover/cover';
import { range, GridRect, getGridInfo } from './utils';
import { store as blockEditorStore } from '../../store';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@
import { useSelect } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { Button } from '@wordpress/components';
import { useRef } from '@wordpress/element';

/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import { __unstableUseBlockRef as useBlockRef } from '../block-list/use-block-props/use-block-refs';
import { useBlockElementRef } from '../block-list/use-block-props/use-block-refs';

/**
* @see https://github.com/WordPress/gutenberg/blob/HEAD/packages/block-editor/src/components/skip-to-selected-block/README.md
Expand All @@ -19,9 +20,10 @@ export default function SkipToSelectedBlock() {
( select ) => select( blockEditorStore ).getBlockSelectionStart(),
[]
);
const ref = useBlockRef( selectedBlockClientId );
const ref = useRef();
useBlockElementRef( selectedBlockClientId, ref );
const onClick = () => {
ref.current.focus();
ref.current?.focus();
};

return selectedBlockClientId ? (
Expand Down
14 changes: 7 additions & 7 deletions packages/block-editor/src/hooks/contrast-checker.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { useState, useEffect } from '@wordpress/element';
* Internal dependencies
*/
import ContrastChecker from '../components/contrast-checker';
import { __unstableUseBlockRef as useBlockRef } from '../components/block-list/use-block-props/use-block-refs';
import { useBlockElement } from '../components/block-list/use-block-props/use-block-refs';

function getComputedStyle( node ) {
return node.ownerDocument.defaultView.getComputedStyle( node );
Expand All @@ -17,23 +17,23 @@ export default function BlockColorContrastChecker( { clientId } ) {
const [ detectedBackgroundColor, setDetectedBackgroundColor ] = useState();
const [ detectedColor, setDetectedColor ] = useState();
const [ detectedLinkColor, setDetectedLinkColor ] = useState();
const ref = useBlockRef( clientId );
const blockEl = useBlockElement( clientId );

// There are so many things that can change the color of a block
// So we perform this check on every render.
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect( () => {
if ( ! ref.current ) {
if ( ! blockEl ) {
return;
}
setDetectedColor( getComputedStyle( ref.current ).color );
setDetectedColor( getComputedStyle( blockEl ).color );

const firstLinkElement = ref.current?.querySelector( 'a' );
const firstLinkElement = blockEl.querySelector( 'a' );
if ( firstLinkElement && !! firstLinkElement.innerText ) {
setDetectedLinkColor( getComputedStyle( firstLinkElement ).color );
}

let backgroundColorNode = ref.current;
let backgroundColorNode = blockEl;
let backgroundColor =
getComputedStyle( backgroundColorNode ).backgroundColor;
while (
Expand All @@ -48,7 +48,7 @@ export default function BlockColorContrastChecker( { clientId } ) {
}

setDetectedBackgroundColor( backgroundColor );
} );
}, [ blockEl ] );

return (
<ContrastChecker
Expand Down
2 changes: 1 addition & 1 deletion packages/block-editor/src/hooks/duotone.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import { scopeSelector } from '../components/global-styles/utils';
import { useBlockSettings, useStyleOverride } from './utils';
import { default as StylesFiltersPanel } from '../components/global-styles/filters-panel';
import { useBlockEditingMode } from '../components/block-editing-mode';
import { __unstableUseBlockElement as useBlockElement } from '../components/block-list/use-block-props/use-block-refs';
import { useBlockElement } from '../components/block-list/use-block-props/use-block-refs';

const EMPTY_ARRAY = [];

Expand Down
2 changes: 1 addition & 1 deletion packages/block-editor/src/hooks/spacing-visualizer.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import isShallowEqual from '@wordpress/is-shallow-equal';
* Internal dependencies
*/
import BlockPopoverCover from '../components/block-popover/cover';
import { __unstableUseBlockElement as useBlockElement } from '../components/block-list/use-block-props/use-block-refs';
import { useBlockElement } from '../components/block-list/use-block-props/use-block-refs';

function SpacingVisualizer( { clientId, value, computeStyle, forceShow } ) {
const blockElement = useBlockElement( clientId );
Expand Down
16 changes: 15 additions & 1 deletion packages/element/src/react.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,26 @@ import {
*/

/**
* Object containing a React synthetic event.
* Object containing a React ref object.
*
* @template T
* @typedef {import('react').RefObject<T>} RefObject<T>
*/

/**
* Object containing a React ref callback.
*
* @template T
* @typedef {import('react').RefCallback<T>} RefCallback<T>
*/

/**
* Object containing a React ref.
*
* @template T
* @typedef {import('react').Ref<T>} Ref<T>
*/

/**
* Object that provides utilities for dealing with React children.
*/
Expand Down
Loading