PluginProbe
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor / 4.0.3
User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor v4.0.3
4.0.3 4.0.2 4.0.1 4.0.0 3.16.6 3.16.5 3.16.4 3.16.3 3.16.2 3.16.1 3.16.0 3.15.9 3.9.9 3.9.5 3.9.6 3.9.7 3.9.8 1.1.7 1.1.8 1.1.9 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 All 341 releases
profile-builder / form-builder / blocks / src / components / useAllocateFieldId.js

useAllocateFieldId.js in User Profile Builder – Beautiful User Registration Forms, User Profiles & User Role Editor 4.0.3, at form-builder/blocks/src/components/useAllocateFieldId.js

79 lines 3.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { useEffect, useRef } from '@wordpress/element';
2 import { select } from '@wordpress/data';
3
4 /**
5 * IDs claimed this session before save round-trip. Save-time projection is the real dedup.
6 */
7 const sessionClaimedIds = new Set();
8
9 function collectAllIdsFromBlocks( blocks, out = [] ) {
10 for ( const b of blocks ) {
11 const id = b.attributes?.id;
12 if ( typeof id === 'number' && id > 0 ) out.push( id );
13 if ( b.innerBlocks && b.innerBlocks.length ) {
14 collectAllIdsFromBlocks( b.innerBlocks, out );
15 }
16 }
17 return out;
18 }
19
20 /**
21 * Allocate a field ID via REST when `id === 0` on first mount. Failures stay at 0 until save.
22 */
23 const MAX_ALLOCATE_ATTEMPTS = 3;
24
25 export function useAllocateFieldId( currentId, setAttributes ) {
26 const attempted = useRef( false );
27 const failures = useRef( 0 );
28
29 useEffect( () => {
30 if ( currentId > 0 ) return;
31 if ( attempted.current ) return;
32 attempted.current = true;
33
34 const cfg = ( typeof window !== 'undefined' && window.wppbFb && window.wppbFb.allocate ) || {};
35 if ( ! cfg.restRoot || ! cfg.restNonce ) return;
36
37 // Read the tree's claimed ids LAZILY, once, here. A `useSelect` would
38 // re-walk the whole block tree in every mounted field block on every
39 // core/block-editor emission (Gutenberg emits per keystroke) — O(n²) node
40 // visits per keypress on a large form — to feed a value only ever consumed
41 // on this one-shot path.
42 const currentTreeIds = collectAllIdsFromBlocks( select( 'core/block-editor' ).getBlocks() );
43
44 const claimed = Array.from( new Set( [ ...currentTreeIds, ...sessionClaimedIds ] ) );
45
46 fetch( cfg.restRoot, {
47 method: 'POST',
48 credentials: 'same-origin',
49 headers: {
50 'X-WP-Nonce': cfg.restNonce,
51 'Content-Type': 'application/json',
52 },
53 body: JSON.stringify( { claimed } ),
54 } )
55 .then( ( r ) => ( r.ok ? r.json() : null ) )
56 .then( ( data ) => {
57 const allocated = data && typeof data.id === 'number' ? data.id : 0;
58 if ( allocated > 0 ) {
59 sessionClaimedIds.add( allocated );
60 setAttributes( { id: allocated } );
61 return;
62 }
63 releaseForRetry();
64 } )
65 .catch( releaseForRetry );
66
67 // A failed allocation used to be one-shot: `attempted` stayed true, the
68 // block kept id === 0, and scheduleAttributePersist() bails on id <= 0 —
69 // so ALL per-edit persistence for this block was silently dead for the
70 // rest of the session (the save-time allocator only covers the on-canvas
71 // path, never a not-in-form card). Re-open the gate, bounded, so a later
72 // render tries again.
73 function releaseForRetry() {
74 failures.current += 1;
75 if ( failures.current < MAX_ALLOCATE_ATTEMPTS ) attempted.current = false;
76 }
77 }, [ currentId ] );
78 }
79