diff --git a/packages/block-editor/src/components/use-no-recursive-renders/index.js b/packages/block-editor/src/components/use-no-recursive-renders/index.js index 94ae4b7c19263..75930f70b5283 100644 --- a/packages/block-editor/src/components/use-no-recursive-renders/index.js +++ b/packages/block-editor/src/components/use-no-recursive-renders/index.js @@ -8,18 +8,37 @@ import { useMemo, } from '@wordpress/element'; -const RenderedRefsContext = createContext( new Set() ); +/** + * Internal dependencies + */ +import { useBlockEditContext } from '../block-edit/context'; + +const RenderedRefsContext = createContext( {} ); + +/** + * Immutably adds an unique identifier to a set scoped for a given block type. + * + * @param {Object} renderedBlocks Rendered blocks grouped by block name + * @param {string} blockName Name of the block. + * @param {*} uniqueId Any value that acts as a unique identifier for a block instance. + * + * @return {Object} The list of rendered blocks grouped by block name. + */ +function addToBlockType( renderedBlocks, blockName, uniqueId ) { + const result = { + ...renderedBlocks, + [ blockName ]: renderedBlocks[ blockName ] + ? new Set( renderedBlocks[ blockName ] ) + : new Set(), + }; + result[ blockName ].add( uniqueId ); -// Immutably add to a Set -function add( set, element ) { - const result = new Set( set ); - result.add( element ); return result; } /** * A React hook for keeping track of blocks previously rendered up in the block - * tree. Blocks susceptible to recursiion can use this hook in their `Edit` + * tree. Blocks susceptible to recursion can use this hook in their `Edit` * function to prevent said recursion. * * @param {*} uniqueId Any value that acts as a unique identifier for a block instance. @@ -32,10 +51,13 @@ function add( set, element ) { */ export default function useNoRecursiveRenders( uniqueId ) { const previouslyRenderedBlocks = useContext( RenderedRefsContext ); - const hasAlreadyRendered = previouslyRenderedBlocks.has( uniqueId ); + const { name: blockName } = useBlockEditContext(); + const hasAlreadyRendered = Boolean( + previouslyRenderedBlocks[ blockName ]?.has( uniqueId ) + ); const newRenderedBlocks = useMemo( - () => add( previouslyRenderedBlocks, uniqueId ), - [ uniqueId, previouslyRenderedBlocks ] + () => addToBlockType( previouslyRenderedBlocks, blockName, uniqueId ), + [ previouslyRenderedBlocks, blockName, uniqueId ] ); const Provider = useCallback( ( { children } ) => ( diff --git a/packages/block-editor/src/components/use-no-recursive-renders/test/use-no-recursive-renders.js b/packages/block-editor/src/components/use-no-recursive-renders/test/use-no-recursive-renders.js index 0844dd601b3a2..be41180eb7a22 100644 --- a/packages/block-editor/src/components/use-no-recursive-renders/test/use-no-recursive-renders.js +++ b/packages/block-editor/src/components/use-no-recursive-renders/test/use-no-recursive-renders.js @@ -4,34 +4,45 @@ import { render } from '@testing-library/react'; /** - * WordPress dependencies + * Internal dependencies */ -import { Fragment } from '@wordpress/element'; -import { __experimentalUseNoRecursiveRenders as useNoRecursiveRenders } from '@wordpress/block-editor'; +import useNoRecursiveRenders from '../'; +import { + BlockEditContextProvider, + useBlockEditContext, +} from '../../block-edit/context'; // Mimics a block's Edit component, such as ReusableBlockEdit, which is capable -// of calling itself depending on its `ref` attribute. -function Edit( { attributes: { ref } } ) { +// of calling itself depending on its `uniqueId` attribute. +function Edit( { attributes: { uniqueId } } ) { + const { name } = useBlockEditContext(); const [ hasAlreadyRendered, RecursionProvider ] = useNoRecursiveRenders( - ref + uniqueId ); if ( hasAlreadyRendered ) { - return
Done
} - { ref === 'SINGLY-RECURSIVE' && ( -Done
} + { uniqueId === 'SINGLY-RECURSIVE' && ( +%s
. Block cannot be rendered inside itself.' ),
+ wp_json_encode( $attributes )
+ ),
+ E_USER_WARNING
+ );
+ }
+
+ // WP_DEBUG_DISPLAY must only be honored when WP_DEBUG. This precedent
+ // is set in `wp_debug_mode()`.
+ $is_debug = defined( 'WP_DEBUG' ) && WP_DEBUG &&
+ defined( 'WP_DEBUG_DISPLAY' ) && WP_DEBUG_DISPLAY;
+ return $is_debug ?
+ // translators: Visible only in the front end, this warning takes the place of a faulty block.
+ __( '[block rendering halted]' ) :
+ '';
}
// Run through the actions that are typically taken on the_content.
- $content = do_blocks( $content );
+ $seen_ids[ $template_part_id ] = true;
+ $content = do_blocks( $content );
+ unset( $seen_ids[ $template_part_id ] );
$content = wptexturize( $content );
$content = convert_smilies( $content );
$content = wpautop( $content );