Manchmal musst du möglicherweise einzigartige Daten in einem Block speichern, um ihn später identifizieren zu können. Daher musst du dafür sorgen, dass es keine doppelten Block-Attribute gibt. In meinem Fall möchte ich eine einzigartige Block-ID speichern. Dafür ist die clientId nützlich, da sie bereits per Definition einzigartig ist (es ist ein Universally Unique Identifier, der bei jedem Ladevorgang des Editors für jeden Block generiert wird).

Da sie aber eben bei jedem Ladevorgang neu generiert wird, hat jeder Block nach jedem Ladevorgang auch eine neue clientId. Daher müssen wir diesen Wert erst irgendwo speichern, am besten als einfaches Attribut:

const attributes = {
	blockId: {
		type: 'string',
	},
};
Code-Sprache: JavaScript (javascript)

Wenn das Attribut noch nicht vorhanden ist, müssen wir es lediglich aus der automatisch generierten clientId speichern.

if ( ! blockId ) {
	setAttributes( { blockId: clientId } );
}
Code-Sprache: JavaScript (javascript)

Um sicherzustellen, dass diese Zuweisung nur einmal durchgeführt wird, wenn die Komponente gerendert wird, können wir sie mit einem useEffect umschließen, das ein leeres Array als zweiten Parameter enthält, um React zu sagen, dass es keine Abhängigkeiten hat und nur einmal ausgeführt werden soll:

import { useEffect } from '@wordpress/element';

useEffect( () => {
	if ( ! blockId ) {
		setAttributes( { blockId: clientId } );
	}
}, [] );
Code-Sprache: JavaScript (javascript)

Das funktioniert so wunderbar in der Edit-Funktion meines Blocks, aber jetzt können mehrere Blöcke die identische Block-ID besitzen, beispielsweise wenn man den Block manuell kopiert und wieder in denselben Editor einfügt. Um das zu verhindern, müssen wir jeden Block auf eine identische Block-ID prüfen und wenn sie bereits in Verwendung ist, eine neue generieren bzw. aus der clientId holen:

import {
	store as blockEditorStore,
} from '@wordpress/block-editor';
import { useEffect } from '@wordpress/element';
import { select } from '@wordpress/data';

const isBlockIdInUse = ( clientId, blockId, blocks ) => {
	let hasBlockId = false;
	
	for ( const block of blocks ) {
		if ( block.clientId === clientId ) {
			continue;
		}
		
		if ( block.innerBlocks ) {
			hasBlockId = isBlockIdInUse( clientId, blockId, block.innerBlocks );
		}
		
		if ( hasBlockId ) {
			return true;
		}
		
		hasBlockId = block.attributes.blockId === blockId;
		
		if ( hasBlockId ) {
			return true;
		}
	}
	
	return false;
}
	
useEffect( () => {
	if ( ! blockId ) {
		setAttributes( { blockId: clientId } );
		
		return;
	}
	
	const allBlocks = select( blockEditorStore ).getBlocks();
	
	if ( isBlockIdInUse( clientId, blockId, allBlocks ) ) {
		setAttributes( { blockId: clientId } );
	}
}, [] );
Code-Sprache: JavaScript (javascript)

Wie du sehen kannst, führt die neue Funktion isBlockIdInUse eine Prüfung aller übergebenen Blöcke rekursiv durch, ob sie eine übergebene Block-ID verwenden – mit Ausnahme des aktuellen Blocks. Diese Prüfung kommt dann in unserer bestehenden useEffect-Funktion hinzu.

Das funktioniert für einen normalen Block in der Edit-Funktion wunderbar, wenn du ihn kopierst und wieder einfügst.

Leider funktioniert das nicht, wenn du den Block über die Werkzeugleiste duplizierst oder anderweitig manipulierst, da dies außerhalb der Block-Bearbeitung stattfindet und da die Edit-Funktion nicht aufgerufen wird – oder zumindest nicht die useEffect-Funktion.

Glücklicherweise gibt es dafür einen Filter, der das bewerkstelligen kann. Über den editor.BlockEdit-Filter können wir dieselbe Funktionalität verwenden, aber sie funktioniert auch beim Duplizieren des Blocks via Werkzeugleiste:

import {
	store as blockEditorStore,
} from '@wordpress/block-editor';
import { createHigherOrderComponent } from '@wordpress/compose';
import { useEffect } from '@wordpress/element';
import { select } from '@wordpress/data';
import { addFilter } from '@wordpress/hooks';

const setBlockId = createHigherOrderComponent( ( BlockEdit ) => ( props ) => {
	const {
		attributes: {
			blockId,
		},
		clientId,
		setAttributes,
		name,
	} = props;
	
	if ( name !== 'my-block/block' ) {
		return <BlockEdit { ...props } />;
	}
	
	const isBlockIdInUse = ( clientId, blockId, blocks ) => {
		let hasBlockId = false;
		
		for ( const block of blocks ) {
			if ( block.clientId === clientId ) {
				continue;
			}
			
			if ( block.innerBlocks ) {
				hasBlockId = isBlockIdInUse( clientId, blockId, block.innerBlocks );
			}
			
			if ( hasBlockId ) {
				return true;
			}
			
			hasBlockId = block.attributes.blockId === blockId;
			
			if ( hasBlockId ) {
				return true;
			}
		}
		
		return false;
	}
	
	useEffect( () => {
		if ( ! blockId ) {
			setAttributes( { blockId: clientId } );
			
			return;
		}
		
		const allBlocks = select( blockEditorStore ).getBlocks();
		
		if ( isBlockIdInUse( clientId, blockId, allBlocks ) ) {
			setAttributes( { blockId: clientId } );
			
			return;
		}
	}, [] );
	
	return <BlockEdit { ...props } />;
} );

addFilter( 'editor.BlockEdit', 'myBlock/block-id-update/set-block-id', setBlockId );
Code-Sprache: JavaScript (javascript)

Dieser Filter ist auf meinen Block mit dem Namen my-block/block beschränkt, hier musst du demnach auf jeden Fall entsprechende Anpassungen vornehmen.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert