diff --git a/packages/block-editor/src/components/provider/test/use-block-sync.js b/packages/block-editor/src/components/provider/test/use-block-sync.js index 7901c3d98f3a9..b1e6a97d137c9 100644 --- a/packages/block-editor/src/components/provider/test/use-block-sync.js +++ b/packages/block-editor/src/components/provider/test/use-block-sync.js @@ -48,7 +48,7 @@ describe( 'useBlockSync hook', () => { jest.clearAllMocks(); } ); - it( 'resets the block-editor blocks when the controll value changes', async () => { + it( 'resets the block-editor blocks when the controlled value changes', async () => { const fakeBlocks = []; const resetBlocks = jest.spyOn( blockEditorActions, 'resetBlocks' ); const replaceInnerBlocks = jest.spyOn( @@ -58,7 +58,7 @@ describe( 'useBlockSync hook', () => { const onChange = jest.fn(); const onInput = jest.fn(); - const { rerender } = render( + const { rerender, unmount } = render( { expect( onInput ).not.toHaveBeenCalled(); expect( replaceInnerBlocks ).not.toHaveBeenCalled(); expect( resetBlocks ).toHaveBeenCalledWith( testBlocks ); + + unmount(); + + expect( onChange ).not.toHaveBeenCalled(); + expect( onInput ).not.toHaveBeenCalled(); + expect( replaceInnerBlocks ).not.toHaveBeenCalled(); + expect( resetBlocks ).toHaveBeenCalledWith( [] ); } ); - it( 'replaces the inner blocks of a block when the control value changes if a clientId is passed', async () => { + it( 'replaces the inner blocks of a block when the controlled value changes if a clientId is passed', async () => { const fakeBlocks = []; const replaceInnerBlocks = jest.spyOn( blockEditorActions, @@ -100,7 +107,7 @@ describe( 'useBlockSync hook', () => { const onChange = jest.fn(); const onInput = jest.fn(); - const { rerender } = render( + const { rerender, unmount } = render( { expect( onChange ).not.toHaveBeenCalled(); expect( onInput ).not.toHaveBeenCalled(); expect( resetBlocks ).not.toHaveBeenCalled(); - // We can't check the args because the blocks are cloned. - expect( replaceInnerBlocks ).toHaveBeenCalled(); + expect( replaceInnerBlocks ).toHaveBeenCalledWith( 'test', [ + expect.objectContaining( { name: 'test/test-block' } ), + ] ); + + unmount(); + + expect( onChange ).not.toHaveBeenCalled(); + expect( onInput ).not.toHaveBeenCalled(); + expect( resetBlocks ).not.toHaveBeenCalled(); + expect( replaceInnerBlocks ).toHaveBeenCalledWith( 'test', [] ); } ); it( 'does not add the controlled blocks to the block-editor store if the store already contains them', async () => { diff --git a/packages/block-editor/src/components/provider/use-block-sync.js b/packages/block-editor/src/components/provider/use-block-sync.js index f7392f99035a9..d788c7b444230 100644 --- a/packages/block-editor/src/components/provider/use-block-sync.js +++ b/packages/block-editor/src/components/provider/use-block-sync.js @@ -134,6 +134,19 @@ export default function useBlockSync( { } }; + // Clean up the changes made by setControlledBlocks() when the component + // containing useBlockSync() unmounts. + const unsetControlledBlocks = () => { + __unstableMarkNextChangeAsNotPersistent(); + if ( clientId ) { + setHasControlledInnerBlocks( clientId, false ); + __unstableMarkNextChangeAsNotPersistent(); + replaceInnerBlocks( clientId, [] ); + } else { + resetBlocks( [] ); + } + }; + // Add a subscription to the block-editor registry to detect when changes // have been made. This lets us inform the data source of changes. This // is an effect so that the subscriber can run synchronously without @@ -287,4 +300,10 @@ export default function useBlockSync( { unsubscribe(); }; }, [ registry, clientId ] ); + + useEffect( () => { + return () => { + unsetControlledBlocks(); + }; + }, [] ); }