Skip to content

Commit

Permalink
Block Editor: Add hook: useNoRecursiveRenders (#28428)
Browse files Browse the repository at this point in the history
* Block Editor: Add useNoRecursiveRenders

Originally introduced in #28405 to prevent Reusable Blocks from
infinitely and fatally recurring, React hook `useNoRecursiveRenders` has
a place in the block-editor package so that other block types
susceptible to recursion can be fixed too.

* Update variables to reflect uses beyond Reusable Block

* useNoRecursiveRenders: Use Set instead of Array

* Add JSDoc comment.
  • Loading branch information
mcsf authored Jan 25, 2021
1 parent 285f855 commit 956ba00
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 39 deletions.
1 change: 1 addition & 0 deletions packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ export { default as WritingFlow } from './writing-flow';
export { useCanvasClickRedirect as __unstableUseCanvasClickRedirect } from './use-canvas-click-redirect';
export { default as useBlockDisplayInformation } from './use-block-display-information';
export { default as __unstableIframe } from './iframe';
export { default as __experimentalUseNoRecursiveRenders } from './use-no-recursive-renders';

/*
* State Related Components
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/**
* WordPress dependencies
*/
import {
createContext,
useCallback,
useContext,
useMemo,
} from '@wordpress/element';

const RenderedRefsContext = createContext( new Set() );

// 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`
* function to prevent said recursion.
*
* @param {*} uniqueId Any value that acts as a unique identifier for a block instance.
*
* @return {[boolean, Function]} A tuple of:
* - a boolean describing whether the provided id
* has already been rendered;
* - a React context provider to be used to wrap
* other elements.
*/
export default function useNoRecursiveRenders( uniqueId ) {
const previouslyRenderedBlocks = useContext( RenderedRefsContext );
const hasAlreadyRendered = previouslyRenderedBlocks.has( uniqueId );
const newRenderedBlocks = useMemo(
() => add( previouslyRenderedBlocks, uniqueId ),
[ uniqueId, previouslyRenderedBlocks ]
);
const Provider = useCallback(
( { children } ) => (
<RenderedRefsContext.Provider value={ newRenderedBlocks }>
{ children }
</RenderedRefsContext.Provider>
),
[ newRenderedBlocks ]
);
return [ hasAlreadyRendered, Provider ];
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { render } from '@testing-library/react';
* WordPress dependencies
*/
import { Fragment } from '@wordpress/element';
import { __experimentalUseNoRecursiveRenders as useNoRecursiveRenders } from '@wordpress/block-editor';

// Mimics a block's Edit component, such as ReusableBlockEdit, which is capable
// of calling itself depending on its `ref` attribute.
Expand Down Expand Up @@ -37,11 +38,6 @@ function Edit( { attributes: { ref } } ) {
);
}

/**
* Internal dependencies
*/
import useNoRecursiveRenders from '../use-no-recursive-renders';

describe( 'useNoRecursiveRenders', () => {
it( 'allows a single block to render', () => {
const { container } = render(
Expand Down
6 changes: 1 addition & 5 deletions packages/block-library/src/block/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
import { __ } from '@wordpress/i18n';
import {
__experimentalUseInnerBlocksProps as useInnerBlocksProps,
__experimentalUseNoRecursiveRenders as useNoRecursiveRenders,
InnerBlocks,
BlockControls,
InspectorControls,
Expand All @@ -26,11 +27,6 @@ import {
} from '@wordpress/block-editor';
import { store as reusableBlocksStore } from '@wordpress/reusable-blocks';

/**
* Internal dependencies
*/
import useNoRecursiveRenders from './use-no-recursive-renders';

export default function ReusableBlockEdit( { attributes: { ref }, clientId } ) {
const [ hasAlreadyRendered, RecursionProvider ] = useNoRecursiveRenders(
ref
Expand Down
29 changes: 0 additions & 29 deletions packages/block-library/src/block/use-no-recursive-renders.js

This file was deleted.

0 comments on commit 956ba00

Please sign in to comment.