Skip to content

Commit d6710d7

Browse files
committed
improve design library performance
- apply useCallbacks, useMemo, and memo - fix preview when loading
1 parent 4942604 commit d6710d7

File tree

7 files changed

+137
-95
lines changed

7 files changed

+137
-95
lines changed

src/block/design-library/edit.js

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ import { dispatch, select } from '@wordpress/data'
2626
import {
2727
createBlock, createBlocksFromInnerBlocksTemplate, getBlockVariations, getBlockType,
2828
} from '@wordpress/blocks'
29-
import { useRef, useState } from '@wordpress/element'
29+
import {
30+
useRef, useState, useCallback,
31+
} from '@wordpress/element'
3032
import { applyFilters } from '@wordpress/hooks'
3133
import {
3234
// eslint-disable-next-line @wordpress/no-unsafe-wp-apis
@@ -326,6 +328,47 @@ const Edit = props => {
326328
await addDesigns( true )
327329
}
328330

331+
const onClose = useCallback( () => setIsLibraryOpen( false ), [] )
332+
333+
const onSelect = useCallback( async ( _designs, callback, type ) => {
334+
const designs = []
335+
let disabledBlocks = new Set()
336+
337+
_designs.forEach( design => {
338+
const {
339+
designData, blocksForSubstitution, category,
340+
} = design
341+
342+
if ( blocksForSubstitution.size ) {
343+
disabledBlocks = disabledBlocks.union( blocksForSubstitution )
344+
}
345+
346+
designs.push( { designData, category } )
347+
} )
348+
349+
designsRef.current = designs
350+
disabledBlocksRef.current = disabledBlocks
351+
callbackRef.current = callback
352+
353+
if ( type === 'pages' ) {
354+
const allBlocks = select( 'core/block-editor' ).getBlockOrder()
355+
const blocksToRemove = allBlocks.filter( id => id !== clientId )
356+
357+
if ( blocksToRemove.length ) {
358+
blocksToRemoveRef.current = allBlocks
359+
setIsDialogOpen( DIALOG_OPTIONS.REMOVE_BLOCKS )
360+
return
361+
}
362+
}
363+
364+
if ( disabledBlocks.size ) {
365+
setIsDialogOpen( DIALOG_OPTIONS.DISABLED_BLOCKS )
366+
return
367+
}
368+
369+
await addDesigns( false )
370+
}, [] )
371+
329372
if ( attributes.previewMode ) {
330373
const src = previewImage.match( /https?:/i ) ? previewImage
331374
: srcUrl ? `${ srcUrl }/${ previewImage }`
@@ -356,47 +399,8 @@ const Edit = props => {
356399

357400
{ isLibraryOpen &&
358401
<ModalDesignLibrary
359-
onClose={ () => {
360-
setIsLibraryOpen( false )
361-
} }
362-
onSelect={ async ( _designs, callback, type ) => {
363-
const designs = []
364-
let disabledBlocks = new Set()
365-
366-
_designs.forEach( design => {
367-
const {
368-
designData, blocksForSubstitution, category,
369-
} = design
370-
371-
if ( blocksForSubstitution.size ) {
372-
disabledBlocks = disabledBlocks.union( blocksForSubstitution )
373-
}
374-
375-
designs.push( { designData, category } )
376-
} )
377-
378-
designsRef.current = designs
379-
disabledBlocksRef.current = disabledBlocks
380-
callbackRef.current = callback
381-
382-
if ( type === 'pages' ) {
383-
const allBlocks = select( 'core/block-editor' ).getBlockOrder()
384-
const blocksToRemove = allBlocks.filter( id => id !== clientId )
385-
386-
if ( blocksToRemove.length ) {
387-
blocksToRemoveRef.current = allBlocks
388-
setIsDialogOpen( DIALOG_OPTIONS.REMOVE_BLOCKS )
389-
return
390-
}
391-
}
392-
393-
if ( disabledBlocks.size ) {
394-
setIsDialogOpen( DIALOG_OPTIONS.DISABLED_BLOCKS )
395-
return
396-
}
397-
398-
await addDesigns( false )
399-
} }
402+
onClose={ onClose }
403+
onSelect={ onSelect }
400404
/>
401405
}
402406
{ isDialogOpen !== DIALOG_OPTIONS.CLOSE &&

src/components/design-library-list/design-library-list-item.js

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,30 +11,30 @@ import { useAutoScroll } from './use-auto-scroll'
1111
/**
1212
* External dependencies.
1313
*/
14-
import { usePresetControls } from '~stackable/hooks'
1514
import { isPro, i18n } from 'stackable'
1615
import classnames from 'classnames'
1716
import { Tooltip } from '~stackable/components'
1817

1918
/**
2019
* WordPress dependencies.
2120
*/
22-
import { useState, useRef } from '@wordpress/element'
21+
import {
22+
useState, useRef, memo,
23+
} from '@wordpress/element'
2324
import { Dashicon, Spinner } from '@wordpress/components'
2425
import { __ } from '@wordpress/i18n'
2526

26-
const DesignLibraryListItem = props => {
27+
const DesignLibraryListItem = memo( props => {
2728
const {
2829
selectedTab,
2930
plan, label,
3031
selectedNum,
3132
selectedData,
3233
isMultiSelectBusy,
3334
shouldRender,
35+
presetMarks,
3436
} = props
3537

36-
const presetMarks = usePresetControls( 'spacingSizes' )?.getPresetMarks() || null
37-
3838
const spacingSize = ! presetMarks || ! Array.isArray( presetMarks ) ? 120 : presetMarks[ presetMarks.length - 2 ].value
3939

4040
const [ isLoading, setIsLoading ] = useState( true )
@@ -109,7 +109,7 @@ const DesignLibraryListItem = props => {
109109
style={ {
110110
transform: `scale(${ selectedNum && selectedData ? selectedData.selectedPreviewSize.scale : previewSize?.scale })`,
111111
transformOrigin: 'top left',
112-
height: getDesignPreviewSize(),
112+
height: ! blocks ? ( selectedTab === 'pages' ? 345 : 100 ) : getDesignPreviewSize(),
113113
} }
114114
>
115115
<div className="stk-block-design__host" ref={ hostRef }>
@@ -161,7 +161,7 @@ const DesignLibraryListItem = props => {
161161
</footer>
162162
</button>
163163
)
164-
}
164+
} )
165165

166166
DesignLibraryListItem.defaultProps = {
167167
designId: '',

src/components/design-library-list/design-preview.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ export const DesignPreview = ( {
109109
} )
110110
} )
111111
} )
112-
}, [ blocks ] ) // Only depend on blocks; selectedTab and designIndex changes will cause blocks to update
112+
}, [ blocks, shadowRoot ] ) // Only depend on blocks and shadowRoot; selectedTab and designIndex changes will cause blocks to update
113113

114114
return createPortal( <>
115115
<body

src/components/design-library-list/index.js

Lines changed: 40 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ import classnames from 'classnames'
1515
import { Spinner } from '@wordpress/components'
1616
import { __ } from '@wordpress/i18n'
1717
import {
18-
useState, useEffect, useRef,
18+
useState, useEffect, useRef, memo, useMemo,
1919
} from '@wordpress/element'
20+
import { usePresetControls } from '~stackable/hooks'
2021

21-
const DesignLibraryList = props => {
22+
const DesignLibraryList = memo( props => {
2223
const {
2324
className = '',
2425
designs,
@@ -80,7 +81,7 @@ const DesignLibraryList = props => {
8081
</div>
8182
</> }
8283
</div>
83-
}
84+
} )
8485

8586
DesignLibraryList.defaultProps = {
8687
designs: [],
@@ -92,16 +93,21 @@ DesignLibraryList.defaultProps = {
9293
export default DesignLibraryList
9394

9495
const DesignLibraryItem = props => {
96+
const { selectedTab, designIndex } = props
9597
const wrapperRef = useRef( null )
96-
const [ shouldRender, setShouldRender ] = useState( false )
98+
const [ shouldRender, setShouldRender ] = useState( designIndex < 9 )
99+
const { getPresetMarks } = usePresetControls( 'spacingSizes' )
100+
101+
// Intentionally no dependencies: presetMarks won't change while the design library is open
102+
const presetMarks = useMemo( () => getPresetMarks() || null, [] )
97103

98104
useEffect( () => {
99105
let id
100106
if ( typeof requestIdleCallback !== 'undefined' ) {
101-
id = requestIdleCallback( () => setShouldRender( true ) )
107+
id = requestIdleCallback( () => ! shouldRender ? setShouldRender( true ) : {} )
102108
} else {
103109
// fallback
104-
id = setTimeout( () => setShouldRender( true ), props.designIndex * 20 )
110+
id = setTimeout( () => setShouldRender( true ), designIndex * 20 )
105111
}
106112

107113
return () => {
@@ -113,9 +119,36 @@ const DesignLibraryItem = props => {
113119
}
114120
}, [] )
115121

122+
useEffect( () => {
123+
if ( selectedTab === 'pages' ) {
124+
return
125+
}
126+
127+
const rootEl = document.querySelector( '.ugb-modal-design-library__designs' )
128+
if ( ! wrapperRef.current || ! rootEl ) {
129+
return
130+
}
131+
132+
const observer = new IntersectionObserver( ( [ entry ] ) => {
133+
// reduce flicker during rapid scrolls
134+
requestAnimationFrame( () => {
135+
requestAnimationFrame( () => setShouldRender( entry.isIntersecting ) )
136+
} )
137+
}, {
138+
root: rootEl,
139+
rootMargin: '500px',
140+
scrollMargin: '500px',
141+
threshold: 0,
142+
} )
143+
144+
observer.observe( wrapperRef.current )
145+
146+
return () => observer.disconnect()
147+
}, [ selectedTab ] )
148+
116149
return (
117150
<div ref={ wrapperRef }>
118-
<DesignLibraryListItem { ...props } shouldRender={ shouldRender } />
151+
<DesignLibraryListItem { ...props } shouldRender={ shouldRender } presetMarks={ presetMarks } />
119152
</div>
120153
)
121154
}

src/components/modal-design-library/editor.scss

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,8 @@
4444
height: 100%;
4545

4646
&.ugb-modal-design-library__full-pages {
47-
grid-template-rows: auto auto;
4847
.ugb-modal-design-library__designs {
49-
grid-row: 1 / -1;
48+
grid-row: 1 / 4;
5049
}
5150
}
5251
}

src/components/modal-design-library/index.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { ModalDesignLibrary } from './modal'
77
* WordPress dependencies
88
*/
99
import { applyFilters } from '@wordpress/hooks'
10-
import { useMemo } from '@wordpress/element'
10+
import { useMemo, useCallback } from '@wordpress/element'
1111
import { useLocalStorage } from '~stackable/util'
1212

1313
export const Switcher = props => {
@@ -25,11 +25,13 @@ export const Switcher = props => {
2525
return applyFilters( 'stackable.design-library.modal-component', ModalDesignLibrary, apiVersion )
2626
}, [ apiVersion ] )
2727

28+
const onChangeApiVersion = useCallback( v => setApiVersion( v ), [] )
29+
2830
return (
2931
<ModalComponent
3032
hasVersionSwitcher={ versions.length > 1 }
3133
apiVersion={ apiVersion }
32-
onChangeApiVersion={ setApiVersion }
34+
onChangeApiVersion={ onChangeApiVersion }
3335
{ ...props }
3436
/>
3537
)

src/components/modal-design-library/modal.js

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,9 @@ import {
2929
Spinner,
3030
ToggleControl,
3131
} from '@wordpress/components'
32-
import { useEffect, useState } from '@wordpress/element'
32+
import {
33+
useEffect, useState, useCallback,
34+
} from '@wordpress/element'
3335
import { sprintf, __ } from '@wordpress/i18n'
3436
import { useBlockColorSchemes } from '~stackable/hooks'
3537
import ColorSchemePreview from '../color-scheme-preview'
@@ -129,6 +131,40 @@ export const ModalDesignLibrary = props => {
129131
props.onSelect( designs, cb, selectedTab )
130132
}
131133

134+
const onSelectDesign = useCallback( ( designId, category, parsedBlocks, blocksForSubstitution, selectedPreviewSize ) => {
135+
if ( selectedTab === 'pages' ) {
136+
const selectedDesign = [ {
137+
designId, category, designData: parsedBlocks, blocksForSubstitution, selectedPreviewSize,
138+
} ]
139+
addDesign( selectedDesign )
140+
141+
return
142+
}
143+
144+
const newSelectedDesigns = [ ...selectedDesignIds ]
145+
// We also get the design data from displayDesigns
146+
// already instead of after clicking the "Add
147+
// Designs" button since displayDesigns can change
148+
// when the user is switching tabs (block/ui
149+
// kits/wireframes) and the data can be lost.
150+
const newSelectedDesignData = [ ...selectedDesignData ]
151+
152+
if ( newSelectedDesigns.includes( designId ) ) {
153+
const i = newSelectedDesigns.indexOf( designId )
154+
newSelectedDesigns.splice( i, 1 )
155+
setSelectedDesignIds( newSelectedDesigns )
156+
newSelectedDesignData.splice( i, 1 )
157+
setSelectedDesignData( newSelectedDesignData )
158+
} else {
159+
newSelectedDesigns.push( designId )
160+
setSelectedDesignIds( newSelectedDesigns )
161+
newSelectedDesignData.push( {
162+
designId, category, designData: parsedBlocks, blocksForSubstitution, selectedPreviewSize,
163+
} )
164+
setSelectedDesignData( newSelectedDesignData )
165+
}
166+
}, [] )
167+
132168
return (
133169
<Modal
134170
title={ __( 'Stackable Design Library', i18n ) }
@@ -393,39 +429,7 @@ export const ModalDesignLibrary = props => {
393429
designs={ displayDesigns }
394430
selectedDesigns={ selectedDesignIds }
395431
selectedDesignData={ selectedDesignData }
396-
onSelectMulti={ ( designId, category, parsedBlocks, blocksForSubstitution, selectedPreviewSize ) => {
397-
if ( selectedTab === 'pages' ) {
398-
const selectedDesign = [ {
399-
designId, category, designData: parsedBlocks, blocksForSubstitution, selectedPreviewSize,
400-
} ]
401-
addDesign( selectedDesign )
402-
403-
return
404-
}
405-
406-
const newSelectedDesigns = [ ...selectedDesignIds ]
407-
// We also get the design data from displayDesigns
408-
// already instead of after clicking the "Add
409-
// Designs" button since displayDesigns can change
410-
// when the user is switching tabs (block/ui
411-
// kits/wireframes) and the data can be lost.
412-
const newSelectedDesignData = [ ...selectedDesignData ]
413-
414-
if ( newSelectedDesigns.includes( designId ) ) {
415-
const i = newSelectedDesigns.indexOf( designId )
416-
newSelectedDesigns.splice( i, 1 )
417-
setSelectedDesignIds( newSelectedDesigns )
418-
newSelectedDesignData.splice( i, 1 )
419-
setSelectedDesignData( newSelectedDesignData )
420-
} else {
421-
newSelectedDesigns.push( designId )
422-
setSelectedDesignIds( newSelectedDesigns )
423-
newSelectedDesignData.push( {
424-
designId, category, designData: parsedBlocks, blocksForSubstitution, selectedPreviewSize,
425-
} )
426-
setSelectedDesignData( newSelectedDesignData )
427-
}
428-
} }
432+
onSelectMulti={ onSelectDesign }
429433
/>
430434

431435
{ selectedTab === 'patterns' && <aside className="ugb-modal-design-library__footer">

0 commit comments

Comments
 (0)