Last active
February 20, 2023 05:36
-
-
Save rmorse/de3df5ac9ff751c4c0fc6234397b7930 to your computer and use it in GitHub Desktop.
Generate unique IDs (as attributes) for blocks - ensure that duplicating a block will generate a new ID
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* WordPress dependencies | |
*/ | |
import { useSelect, dispatch } from '@wordpress/data'; | |
import { useLayoutEffect } from '@wordpress/element'; | |
import './store'; | |
const storeName = 'plugin-name/block'; | |
function Edit( { attributes, setAttributes, clientId } ) { | |
// Get the stored attribute field ID. | |
const attributeFieldId = attributes.fieldId; | |
// Get the copy of the stored field ID in our custom store. | |
const fieldId = useSelect( ( select ) => { | |
return select( storeName ).getClientFieldId( clientId ); | |
}, [ attributeFieldId ] ); | |
// If the attribute field ID has a value, register that in our store so it knows the value. | |
useLayoutEffect( () => { | |
if ( attributeFieldId !== '' ) { | |
dispatch( storeName ).setFieldId( clientId, attributeFieldId ); | |
} | |
}, [ attributeFieldId ] ); | |
// If there is no value in the store or in the attribute, create a new field ID. | |
// Or if there is a field ID in the store (but not attribute), then copy that into | |
// the attribute. | |
useLayoutEffect( () => { | |
if ( attributeFieldId === '' & fieldId === null ) { | |
dispatch( storeName ).createFieldId( clientId, attributes ); | |
} else if ( attributeFieldId === '' & fieldId !== null ) { | |
setAttributes( { fieldId } ); | |
} | |
}, [ attributeFieldId, fieldId ] ); | |
// To handle duplicates, we need to check if a reset is needed, this is handled in | |
// the store. | |
const shouldResetFieldId = useSelect( ( select ) => { | |
return select( storeName ).shouldResetFieldId( clientId ); | |
}, [ attributeFieldId, fieldId ] ); | |
// Now wait for the reset to be true before unsettting the attribute field ID and | |
// the store field ID. | |
useLayoutEffect( () => { | |
if ( shouldResetFieldId ) { | |
setAttributes( { fieldId: '' } ); | |
dispatch( storeName ).setFieldId( clientId, '' ); | |
} | |
}, [ shouldResetFieldId ] ); | |
return ( | |
<> | |
Block Field ID: { attributeFieldId } | |
</> | |
); | |
} | |
export default Edit; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* WordPress dependencies | |
*/ | |
import { createReduxStore, register } from '@wordpress/data'; | |
import apiFetch from '@wordpress/api-fetch'; | |
const actions = { | |
* createFieldId( clientId, attributes ) { | |
// Create new item on the server and return its ID | |
// TODO - insert your own ID generation logic here | |
const args = { attributes }; | |
const path = `/plugin-name/v1/save`; | |
const result = yield actions.postToAPI( path, args ); | |
// yield setEntityIsLoading( section, id, false ); | |
if ( result.code === 'success' ) { | |
const resultId = result.id; | |
return actions.setFieldId( clientId, resultId ); | |
} | |
}, | |
setFieldId( clientId, fieldId ) { | |
return { | |
type: 'SET_FIELD_ID', | |
clientId, | |
fieldId, | |
}; | |
}, | |
postToAPI( path, data ) { | |
return { | |
type: 'POST_TO_API', | |
path, | |
data, | |
}; | |
}, | |
}; | |
const DEFAULT_STATE = { | |
clientFieldIds: {}, | |
clientsOrdered: [], | |
}; | |
export const defaults = DEFAULT_STATE; | |
export const blockStore = createReduxStore( | |
'plugin-name/block', | |
{ | |
reducer( state = DEFAULT_STATE, action ) { | |
switch ( action.type ) { | |
case 'SET_FIELD_ID': { | |
const clientsOrdered = [ ...state.clientsOrdered ]; | |
if ( clientsOrdered.indexOf( action.clientId ) === -1 ) { | |
clientsOrdered.push( action.clientId ); | |
} | |
return { | |
...state, | |
clientFieldIds: { | |
...state.clientFieldIds, | |
[ action.clientId ]: action.fieldId, | |
}, | |
clientsOrdered, | |
}; | |
} | |
} | |
return state; | |
}, | |
actions, | |
selectors: { | |
getClientFieldId( state, clientId ) { | |
if ( state.clientFieldIds ) { | |
if ( state.clientFieldIds[ clientId ] ) { | |
return state.clientFieldIds[ clientId ]; | |
} | |
} | |
return null; | |
}, | |
getClientFieldIds( state ) { | |
return state.clientFieldIds; | |
}, | |
getClientsOrdered( state ) { | |
return state.clientsOrdered; | |
}, | |
shouldResetFieldId( state, clientId ) { | |
/** | |
* To know if we need to reset a fieldId attribute, we need to compare all the | |
* clientIds associated fieldIds, and look for duplicates. No two clientIds | |
* should have the same fieldId. | |
*/ | |
// First check to see if the current clientId has stored fieldId | |
const clientFieldId = state.clientFieldIds[ clientId ]; | |
// If no ID, don't trigger the reset and exit early. | |
if ( clientFieldId === null ) { | |
return false; | |
} | |
// Now we need to compare all the clientIds to see if any have the same fieldId | |
const { clientFieldIds, clientsOrdered } = state; | |
let shouldReset = false; | |
for ( const clientIdRef of clientsOrdered ) { | |
// If we find a match we might need to reset. | |
if ( clientFieldId === clientFieldIds[ clientIdRef ] ) { | |
// If the first match is not current client ID, we need to reset. | |
if ( clientId !== clientIdRef ) { | |
shouldReset = true; | |
} | |
break; | |
} | |
} | |
return shouldReset; | |
}, | |
}, | |
controls: { | |
POST_TO_API( action ) { | |
return apiFetch( { | |
path: action.path, | |
method: 'POST', | |
data: action.data, | |
} ); | |
}, | |
}, | |
resolvers: {}, | |
} | |
); | |
register( blockStore ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment