From 704f13af5e6658b92f4761721433727ebb344dee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ella=20van=C2=A0Durpe?= Date: Tue, 7 Mar 2023 20:18:58 +0200 Subject: [PATCH] Fix useRootPortal --- .../src/components/block-list/index.js | 76 ++++++++++++++----- packages/block-editor/src/hooks/duotone.js | 7 +- 2 files changed, 62 insertions(+), 21 deletions(-) diff --git a/packages/block-editor/src/components/block-list/index.js b/packages/block-editor/src/components/block-list/index.js index c0d311c254cba8..0c284f50049b41 100644 --- a/packages/block-editor/src/components/block-list/index.js +++ b/packages/block-editor/src/components/block-list/index.js @@ -24,9 +24,8 @@ import { useLayoutEffect, useContext, useRef, - Fragment, - createElement, useReducer, + useId, } from '@wordpress/element'; /** @@ -53,42 +52,79 @@ const pendingBlockVisibilityUpdatesPerRegistry = new WeakMap(); function useRootPortal( component ) { const components = useContext( componentsContext ); + const id = useId(); + // Run every time the component rerenders. useLayoutEffect( () => { - if ( ! component ) return; - components.add( component ); - return () => { - components.delete( component ); - }; - } ); + if ( component ) { + components.render( id, component ); + } else { + components.unmount( id ); + } + }, [ components, id, component ] ); + + // Run only on unmount. + useLayoutEffect( () => () => components.unmount( id ), [ components, id ] ); +} + +function Component( { id, componentsById, renderById } ) { + const [ , forceRender ] = useReducer( () => ( {} ) ); + useLayoutEffect( () => { + renderById.current.set( id, forceRender ); + }, [ id, renderById ] ); + return componentsById.current.get( id ); } -function Components( { subscriber, components } ) { +function Components( { componentsById, renderById, renderAll } ) { const [ , forceRender ] = useReducer( () => ( {} ) ); - subscriber.current = forceRender; - return createElement( Fragment, null, ...components.current ); + + useLayoutEffect( () => { + renderAll.current = forceRender; + }, [ renderAll ] ); + + return Array.from( componentsById.current.keys() ).map( ( key ) => ( + + ) ); } function ComponentRenderer( { children } ) { - const subscriber = useRef( () => {} ); - const components = useRef( new Set() ); + const componentsById = useRef( new Map() ); + const renderById = useRef( new Map() ); + const renderAll = useRef( () => {} ); return ( ( { - add( component ) { - components.current.add( component ); - subscriber.current(); + render( id, component ) { + if ( componentsById.current.has( id ) ) { + componentsById.current.set( id, component ); + renderById.current.get( id )(); + } else { + componentsById.current.set( id, component ); + renderAll.current(); + } }, - delete( component ) { - components.current.delete( component ); - subscriber.current(); + unmount( id ) { + if ( componentsById.current.has( id ) ) { + componentsById.current.delete( id ); + renderById.current.delete( id ); + renderAll.current(); + } }, } ), [] ) } > - + { children } ); diff --git a/packages/block-editor/src/hooks/duotone.js b/packages/block-editor/src/hooks/duotone.js index df2d840188246f..52cd2ece753009 100644 --- a/packages/block-editor/src/hooks/duotone.js +++ b/packages/block-editor/src/hooks/duotone.js @@ -297,9 +297,10 @@ const useDuotoneStyles = ( props, blockType, attributes ) => { ); const id = `wp-duotone-${ useInstanceId( useDuotoneStyles ) }`; + const duotoneStyle = attributes?.style?.color?.duotone; BlockList.useRootPortal( - duotoneSupport && ( + duotoneSupport && duotoneStyle && ( { ) ); + // CAUTION: code added before this line will be executed + // for all blocks, not just those that support duotone. Code added + // above this line should be carefully evaluated for its impact on + // performance. return { ...props, className: duotoneSupport