-
Notifications
You must be signed in to change notification settings - Fork 4.2k
/
use-nested-settings-update.js
189 lines (171 loc) · 6.8 KB
/
use-nested-settings-update.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/**
* WordPress dependencies
*/
import { useLayoutEffect, useState } from '@wordpress/element';
import { useRegistry } from '@wordpress/data';
import deprecated from '@wordpress/deprecated';
import isShallowEqual from '@wordpress/is-shallow-equal';
/**
* Internal dependencies
*/
import { store as blockEditorStore } from '../../store';
import { getLayoutType } from '../../layouts';
/** @typedef {import('../../selectors').WPDirectInsertBlock } WPDirectInsertBlock */
const pendingSettingsUpdates = new WeakMap();
// Creates a memoizing caching function that remembers the last value and keeps returning it
// as long as the new values are shallowly equal. Helps keep dependencies stable.
function createShallowMemo() {
let value;
return ( newValue ) => {
if ( value === undefined || ! isShallowEqual( value, newValue ) ) {
value = newValue;
}
return value;
};
}
function useShallowMemo( value ) {
const [ memo ] = useState( createShallowMemo );
return memo( value );
}
/**
* This hook is a side effect which updates the block-editor store when changes
* happen to inner block settings. The given props are transformed into a
* settings object, and if that is different from the current settings object in
* the block-editor store, then the store is updated with the new settings which
* came from props.
*
* @param {string} clientId The client ID of the block to update.
* @param {string} parentLock
* @param {string[]} allowedBlocks An array of block names which are permitted
* in inner blocks.
* @param {string[]} prioritizedInserterBlocks Block names and/or block variations to be prioritized in the inserter, in the format {blockName}/{variationName}.
* @param {?WPDirectInsertBlock} defaultBlock The default block to insert: [ blockName, { blockAttributes } ].
* @param {?boolean} directInsert If a default block should be inserted directly by the appender.
*
* @param {?WPDirectInsertBlock} __experimentalDefaultBlock A deprecated prop for the default block to insert: [ blockName, { blockAttributes } ]. Use `defaultBlock` instead.
*
* @param {?boolean} __experimentalDirectInsert A deprecated prop for whether a default block should be inserted directly by the appender. Use `directInsert` instead.
*
* @param {string} [templateLock] The template lock specified for the inner
* blocks component. (e.g. "all")
* @param {boolean} captureToolbars Whether or children toolbars should be shown
* in the inner blocks component rather than on
* the child block.
* @param {string} orientation The direction in which the block
* should face.
* @param {Object} layout The layout object for the block container.
*/
export default function useNestedSettingsUpdate(
clientId,
parentLock,
allowedBlocks,
prioritizedInserterBlocks,
defaultBlock,
directInsert,
__experimentalDefaultBlock,
__experimentalDirectInsert,
templateLock,
captureToolbars,
orientation,
layout
) {
// Instead of adding a useSelect mapping here, please add to the useSelect
// mapping in InnerBlocks! Every subscription impacts performance.
const registry = useRegistry();
// Implementors often pass a new array on every render,
// and the contents of the arrays are just strings, so the entire array
// can be passed as dependencies but We need to include the length of the array,
// otherwise if the arrays change length but the first elements are equal the comparison,
// does not works as expected.
const _allowedBlocks = useShallowMemo( allowedBlocks );
const _prioritizedInserterBlocks = useShallowMemo(
prioritizedInserterBlocks
);
const _templateLock =
templateLock === undefined || parentLock === 'contentOnly'
? parentLock
: templateLock;
useLayoutEffect( () => {
const newSettings = {
allowedBlocks: _allowedBlocks,
prioritizedInserterBlocks: _prioritizedInserterBlocks,
templateLock: _templateLock,
};
// These values are not defined for RN, so only include them if they
// are defined.
if ( captureToolbars !== undefined ) {
newSettings.__experimentalCaptureToolbars = captureToolbars;
}
// Orientation depends on layout,
// ideally the separate orientation prop should be deprecated.
if ( orientation !== undefined ) {
newSettings.orientation = orientation;
} else {
const layoutType = getLayoutType( layout?.type );
newSettings.orientation = layoutType.getOrientation( layout );
}
if ( __experimentalDefaultBlock !== undefined ) {
deprecated( '__experimentalDefaultBlock', {
alternative: 'defaultBlock',
since: '6.3',
version: '6.4',
} );
newSettings.defaultBlock = __experimentalDefaultBlock;
}
if ( defaultBlock !== undefined ) {
newSettings.defaultBlock = defaultBlock;
}
if ( __experimentalDirectInsert !== undefined ) {
deprecated( '__experimentalDirectInsert', {
alternative: 'directInsert',
since: '6.3',
version: '6.4',
} );
newSettings.directInsert = __experimentalDirectInsert;
}
if ( directInsert !== undefined ) {
newSettings.directInsert = directInsert;
}
if (
newSettings.directInsert !== undefined &&
typeof newSettings.directInsert !== 'boolean'
) {
deprecated( 'Using `Function` as a `directInsert` argument', {
alternative: '`boolean` values',
since: '6.5',
} );
}
// Batch updates to block list settings to avoid triggering cascading renders
// for each container block included in a tree and optimize initial render.
// To avoid triggering updateBlockListSettings for each container block
// causing X re-renderings for X container blocks,
// 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.get( registry )[ clientId ] = newSettings;
window.queueMicrotask( () => {
const settings = pendingSettingsUpdates.get( registry );
if ( Object.keys( settings ).length ) {
const { updateBlockListSettings } =
registry.dispatch( blockEditorStore );
updateBlockListSettings( settings );
pendingSettingsUpdates.set( registry, {} );
}
} );
}, [
clientId,
_allowedBlocks,
_prioritizedInserterBlocks,
_templateLock,
defaultBlock,
directInsert,
__experimentalDefaultBlock,
__experimentalDirectInsert,
captureToolbars,
orientation,
layout,
registry,
] );
}