From 8b6e9b3bcaa2307c7f61518ae609842ae6d9d4ac Mon Sep 17 00:00:00 2001 From: Ella Date: Thu, 2 May 2024 18:22:33 +0300 Subject: [PATCH 1/2] Perf: batch block list settings in single action --- .../use-nested-settings-update.js | 28 ++++++++----------- .../block-editor/src/store/private-actions.js | 14 ++++++++++ packages/block-editor/src/store/reducer.js | 13 +++++++++ 3 files changed, 39 insertions(+), 16 deletions(-) diff --git a/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js b/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js index 25a3dc9dadfa96..5bfa61b2c1ab7b 100644 --- a/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js +++ b/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js @@ -2,7 +2,7 @@ * WordPress dependencies */ import { useLayoutEffect, useMemo, useState } from '@wordpress/element'; -import { useDispatch, useRegistry } from '@wordpress/data'; +import { useRegistry } from '@wordpress/data'; import deprecated from '@wordpress/deprecated'; import isShallowEqual from '@wordpress/is-shallow-equal'; @@ -11,6 +11,7 @@ import isShallowEqual from '@wordpress/is-shallow-equal'; */ import { store as blockEditorStore } from '../../store'; import { getLayoutType } from '../../layouts'; +import { unlock } from '../../lock-unlock'; /** @typedef {import('../../selectors').WPDirectInsertBlock } WPDirectInsertBlock */ @@ -69,7 +70,6 @@ export default function useNestedSettingsUpdate( // Instead of adding a useSelect mapping here, please add to the useSelect // mapping in InnerBlocks! Every subscription impacts performance. - const { updateBlockListSettings } = useDispatch( blockEditorStore ); const registry = useRegistry(); // Implementors often pass a new array on every render, @@ -155,21 +155,18 @@ export default function useNestedSettingsUpdate( // we batch all the updatedBlockListSettings in a single "data" batch // which results in a single re-render. if ( ! pendingSettingsUpdates.get( registry ) ) { - pendingSettingsUpdates.set( registry, [] ); + pendingSettingsUpdates.set( registry, new Map() ); } - pendingSettingsUpdates - .get( registry ) - .push( [ clientId, newSettings ] ); + pendingSettingsUpdates.get( registry ).set( clientId, newSettings ); window.queueMicrotask( () => { - if ( pendingSettingsUpdates.get( registry )?.length ) { - registry.batch( () => { - pendingSettingsUpdates - .get( registry ) - .forEach( ( args ) => { - updateBlockListSettings( ...args ); - } ); - pendingSettingsUpdates.set( registry, [] ); - } ); + if ( pendingSettingsUpdates.get( registry ).size ) { + const { batchUpdateBlockListSettings } = unlock( + registry.dispatch( blockEditorStore ) + ); + batchUpdateBlockListSettings( + pendingSettingsUpdates.get( registry ) + ); + pendingSettingsUpdates.set( registry, new Map() ); } } ); }, [ @@ -183,7 +180,6 @@ export default function useNestedSettingsUpdate( __experimentalDirectInsert, captureToolbars, orientation, - updateBlockListSettings, layout, registry, ] ); diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js index 28a7b1da98f73f..9a8c50fcc2370c 100644 --- a/packages/block-editor/src/store/private-actions.js +++ b/packages/block-editor/src/store/private-actions.js @@ -26,6 +26,20 @@ const privateSettings = [ 'blockInspectorAnimation', ]; +/** + * Action that changes the nested settings of a given block. + * + * @param {Map} settingsByClientId A map of block client IDs to their settings. + * + * @return {Object} Action object + */ +export function batchUpdateBlockListSettings( settingsByClientId ) { + return { + type: 'BATCH_UPDATE_BLOCK_LIST_SETTINGS', + settingsByClientId, + }; +} + /** * Action that updates the block editor settings and * conditionally preserves the experimental ones. diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index 3ea0fb46273049..d7f5b22f736af3 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1769,6 +1769,19 @@ export const blockListSettings = ( state = {}, action ) => { [ clientId ]: action.settings, }; } + + case 'BATCH_UPDATE_BLOCK_LIST_SETTINGS': { + const { settingsByClientId } = action; + const updates = {}; + for ( const [ clientId, _settings ] of settingsByClientId ) { + if ( ! fastDeepEqual( state[ clientId ], _settings ) ) { + updates[ clientId ] = _settings; + } + } + return Object.keys( updates ).length + ? { ...state, ...updates } + : state; + } } return state; }; From 3a230b33065edf9a15cdb05f2f9e1210f4489ec0 Mon Sep 17 00:00:00 2001 From: Ella Date: Tue, 21 May 2024 13:39:27 +0200 Subject: [PATCH 2/2] Use exiting action --- .../data/data-core-block-editor.md | 4 +- .../use-nested-settings-update.js | 19 ++++---- packages/block-editor/src/store/actions.js | 15 +++++-- .../block-editor/src/store/private-actions.js | 14 ------ packages/block-editor/src/store/reducer.js | 44 +++++++++---------- 5 files changed, 43 insertions(+), 53 deletions(-) diff --git a/docs/reference-guides/data/data-core-block-editor.md b/docs/reference-guides/data/data-core-block-editor.md index 486fcddfe04ac6..862a8b2d8a06aa 100644 --- a/docs/reference-guides/data/data-core-block-editor.md +++ b/docs/reference-guides/data/data-core-block-editor.md @@ -1841,11 +1841,11 @@ _Returns_ ### updateBlockListSettings -Action that changes the nested settings of a given block. +Action that changes the nested settings of the given block(s). _Parameters_ -- _clientId_ `string`: Client ID of the block whose nested setting are being received. +- _clientId_ `string | SettingsByClientId`: Client ID of the block whose nested setting are being received, or object of settings by client ID. - _settings_ `Object`: Object with the new settings for the nested block. _Returns_ diff --git a/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js b/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js index 5bfa61b2c1ab7b..8417dec1dd48ff 100644 --- a/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js +++ b/packages/block-editor/src/components/inner-blocks/use-nested-settings-update.js @@ -11,7 +11,6 @@ import isShallowEqual from '@wordpress/is-shallow-equal'; */ import { store as blockEditorStore } from '../../store'; import { getLayoutType } from '../../layouts'; -import { unlock } from '../../lock-unlock'; /** @typedef {import('../../selectors').WPDirectInsertBlock } WPDirectInsertBlock */ @@ -155,18 +154,16 @@ export default function useNestedSettingsUpdate( // we batch all the updatedBlockListSettings in a single "data" batch // which results in a single re-render. if ( ! pendingSettingsUpdates.get( registry ) ) { - pendingSettingsUpdates.set( registry, new Map() ); + pendingSettingsUpdates.set( registry, {} ); } - pendingSettingsUpdates.get( registry ).set( clientId, newSettings ); + pendingSettingsUpdates.get( registry )[ clientId ] = newSettings; window.queueMicrotask( () => { - if ( pendingSettingsUpdates.get( registry ).size ) { - const { batchUpdateBlockListSettings } = unlock( - registry.dispatch( blockEditorStore ) - ); - batchUpdateBlockListSettings( - pendingSettingsUpdates.get( registry ) - ); - pendingSettingsUpdates.set( registry, new Map() ); + const settings = pendingSettingsUpdates.get( registry ); + if ( Object.keys( settings ).length ) { + const { updateBlockListSettings } = + registry.dispatch( blockEditorStore ); + updateBlockListSettings( settings ); + pendingSettingsUpdates.set( registry, {} ); } } ); }, [ diff --git a/packages/block-editor/src/store/actions.js b/packages/block-editor/src/store/actions.js index d6eaac9c7e8c9b..c9a1430a078fa5 100644 --- a/packages/block-editor/src/store/actions.js +++ b/packages/block-editor/src/store/actions.js @@ -1506,11 +1506,18 @@ export const insertDefaultBlock = }; /** - * Action that changes the nested settings of a given block. + * @typedef {Object< string, Object >} SettingsByClientId + */ + +/** + * Action that changes the nested settings of the given block(s). * - * @param {string} clientId Client ID of the block whose nested setting are - * being received. - * @param {Object} settings Object with the new settings for the nested block. + * @param {string | SettingsByClientId} clientId Client ID of the block whose + * nested setting are being + * received, or object of settings + * by client ID. + * @param {Object} settings Object with the new settings + * for the nested block. * * @return {Object} Action object */ diff --git a/packages/block-editor/src/store/private-actions.js b/packages/block-editor/src/store/private-actions.js index 9a8c50fcc2370c..28a7b1da98f73f 100644 --- a/packages/block-editor/src/store/private-actions.js +++ b/packages/block-editor/src/store/private-actions.js @@ -26,20 +26,6 @@ const privateSettings = [ 'blockInspectorAnimation', ]; -/** - * Action that changes the nested settings of a given block. - * - * @param {Map} settingsByClientId A map of block client IDs to their settings. - * - * @return {Object} Action object - */ -export function batchUpdateBlockListSettings( settingsByClientId ) { - return { - type: 'BATCH_UPDATE_BLOCK_LIST_SETTINGS', - settingsByClientId, - }; -} - /** * Action that updates the block editor settings and * conditionally preserves the experimental ones. diff --git a/packages/block-editor/src/store/reducer.js b/packages/block-editor/src/store/reducer.js index d7f5b22f736af3..7c83887876919f 100644 --- a/packages/block-editor/src/store/reducer.js +++ b/packages/block-editor/src/store/reducer.js @@ -1750,37 +1750,37 @@ export const blockListSettings = ( state = {}, action ) => { ); } case 'UPDATE_BLOCK_LIST_SETTINGS': { - const { clientId } = action; - if ( ! action.settings ) { - if ( state.hasOwnProperty( clientId ) ) { - const { [ clientId ]: removedBlock, ...restBlocks } = state; - return restBlocks; + const updates = + typeof action.clientId === 'string' + ? { [ action.clientId ]: action.settings } + : action.clientId; + + // Remove settings that are the same as the current state. + for ( const clientId in updates ) { + if ( ! updates[ clientId ] ) { + if ( ! state[ clientId ] ) { + delete updates[ clientId ]; + } + } else if ( + fastDeepEqual( state[ clientId ], updates[ clientId ] ) + ) { + delete updates[ clientId ]; } - - return state; } - if ( fastDeepEqual( state[ clientId ], action.settings ) ) { + if ( Object.keys( updates ).length === 0 ) { return state; } - return { - ...state, - [ clientId ]: action.settings, - }; - } + const merged = { ...state, ...updates }; - case 'BATCH_UPDATE_BLOCK_LIST_SETTINGS': { - const { settingsByClientId } = action; - const updates = {}; - for ( const [ clientId, _settings ] of settingsByClientId ) { - if ( ! fastDeepEqual( state[ clientId ], _settings ) ) { - updates[ clientId ] = _settings; + for ( const clientId in updates ) { + if ( ! updates[ clientId ] ) { + delete merged[ clientId ]; } } - return Object.keys( updates ).length - ? { ...state, ...updates } - : state; + + return merged; } } return state;