Die Subscribe-Funktion des WordPress Block Editors (Gutenberg) richtig nutzen

Der Block Editor von WordPress, auch als Gutenberg bekannt, ist komplett in JavaScript bzw. mit React geschrieben. WordPress ist vor allem so ein mächtiges CMS geworden, weil man als Entwickler das Verhalten für Actions & Filter sehr einfach seinen Bedürfnissen anpassen kann. Das ist meiner Meinung nach beim Block Editor etwas auf der Strecke geblieben. Zwar gibt es einige Filter, für Actions muss man aber auf die subscribe()-Funktion zurückgreifen.

Die subscribe()-Funktion ist Teil des Data Moduls. Es dient dazu komponentenübergreifend die Zustände, State genannt, (Inhalte, Post Status, Meta Felder, Terms…) zu verwalten.

Wir können uns über die subscribe()-Funktion über Änderungen am State informieren lassen. Das bedeutet, jedes Mal, wenn sich etwas am State ändert, wird unsere Funktion, die wir über subscribe( function () { ... } ) registriert haben, aufgerufen. Was ihr dann damit macht, bleibt komplett euch überlassen.

Performance beachten

Leider kann man nicht nur bestimmte Änderungen, z.B. Veränderungen am Post Attributes wie dem Post Status, abonnieren. Jedes Mal, wenn sich der State ändert wird eure Funktion aufgerufen. Deswegen müsst ihr damit rechnen, dass die Funktion bei jeder Eingabe (ein Buchstabe oder ein Mausklick) mindestens einmal ausgeführt wird. Häufig sogar mehr als einmal. Innerhalb weniger Minuten wird die Funktion sicherlich hunderte Male ausgeführt.

Deswegen muss man unbedingt darauf achten, dass man den Code performant schreibt und nicht allzu komplexe Annahmen prüft. Sonst werdet ihr sehr schnell Performance einbüßen oder Whitescreens sehen.

Ihr könnt einfach mal im Block Editor folgende Funktion in der Konsole eures Browsers ausführen:

wp.data.subscribe( function () { console.log('State changed') } )

Damit wird bei jeder Änderung am State = Aufruf eurer Funktion, ein State changed in der Console ausgegeben. Das sieht schon nach einer Minute so aus:

Änderungen am State (beachtet die Zahl rechts)

Dabei fasst Firefox die console.log-Ausgaben schon zusammen. In diesem Screenshot wurde State changed fast 4.000 Mal ausgeführt.

Mehr als ein paar Annahmen solltet ihr deswegen in eurer Funktion nicht prüfen!

Richtig subscriben

Besonders wichtig ist, dass ihr sicherstellen müsst, dass ihr die subscribe()-Funktion nur einmal ausführt, um eure Funktion zu registrieren.

Am besten eignet sich dafür die wp.domReady()-Funktion. Diese wird vom Block Editor einmal aufgerufen, wenn das Document Object Model, kurz DOM, vollständig geladen ist und auch der initiale State bereits vorhanden ist.

wp.domReady(function() { wp.data.subscribe(function() { // eure Logik }) })

Auf keinen Fall solltet ihr die subscribe()-Funktion in der render()-Funktion eures Blocks ausführen. Führt man die subscribe()-Funktion direkt beim Rendern des JavaScripts aus, wird sie zwar auch nur einmal aufgerufen, ggf. ist der State aber noch nicht verfügbar.

Initialen State sichern

Eine typische Aufgabe, die man mithilfe der subscribe() Funktion gelöst bekommt ist sich benachrichtigen zu lassen, wenn sich der Post Status des Beitrags ändert, z.B. er publiziert wird.

Der Post Status wird im Data Store core/editor gesichert. Daraus können wir ihn mithilfe der Funktion getEditedPostAttribute( ’status‘ ) abrufen:

let initialPostStatus = wp.data.select( 'core/editor' ).getEditedPostAttribute( 'status' );

Um eine Änderung festzustellen, muss man sich selber den initialen State sichern. Das können wir auch wunderbar direkt in unserer Funktion erledigen, die mit wp.domReady aufgerufen wird.

Anschließend überprüfen wir in unserer abonnierten Funktion, ob sich der Zustand geändert hat.

wp.domReady(function() { let initialPostStatus = wp.data.select( 'core/editor' ).getEditedPostAttribute( 'status' ); const unsubscribe = wp.data.subscribe(function () { let currentPostStatus = wp.data.select('core/editor').getEditedPostAttribute('status'); if (initialPostStatus !== currentPostStatus ) { initialPostStatus = currentPostStatus; // Post Status changed } }) })

Will man jedes Mal benachrichtigt werden, wenn sich der Post Status ändert, dann sollte man den initialPostStatus = currentPostStatus entsprechend updaten. Das sollte man auch so schnell wie möglich nach Erfüllen der Bedingung machen und nicht erst, wenn alle von euch geplante Aktionen durchgeführt worden sind.

Eine Möglichkeit wäre es sich einen Notice = Hinweis ausgeben zu lassen, dass sich der Post Status geändert hat (gibt es zwar auch von Haus aus, ist aber ein gutes Beispiel).

wp.domReady(function() { let initialPostStatus = wp.data.select( 'core/editor' ).getEditedPostAttribute( 'status' ); const unsubscribe = wp.data.subscribe(function () { let currentPostStatus = wp.data.select('core/editor').getEditedPostAttribute('status'); if (initialPostStatus !== currentPostStatus) { initialPostStatus = currentPostStatus; wp.data.dispatch('core/notices').createInfoNotice( 'Post Status changed to ' + currentPostStatus, { isDismissible: true, id: 'post-status-changed-notice', }, ); } }) })

Viele dispatch-Events vermeiden

Will man einen Hinweis abhängig von einer Bedingung ein und auch wieder ausblenden, ist es ratsam sich zusätzlich in einer Variable abzuspeichern, ob der Hinweis gerade angezeigt wird oder nicht. Sonst wird ständig ein dispatch-Event ausgelöst.

Hier soll der Hinweis nur ausgegeben werden, wenn der Beitrag veröffentlicht publish ist:

wp.domReady(function () { let notice = false; const unsubscribe = wp.data.subscribe(function () { let currentPostStatus = wp.data.select('core/editor').getEditedPostAttribute('status'); if (false === notice && 'publish' === currentPostStatus) { notice = true; wp.data.dispatch('core/notices').createInfoNotice( 'Post is ' + currentPostStatus, { isDismissible: false, id: 'post-status-publish', }, ); } else if (true === notice && 'publish' !== currentPostStatus) { notice = false; wp.data.dispatch('core/notices').removeNotice('post-status-publish'); } }) })

Komplexe Berechnungen vermeiden

Ihr solltet euch wirklich gut überlegen, welche Berechnungen oder Annahmen ihr in der eurer Funktion prüfen wollt. Wollt ihr z.B. überprüfen, ob sich das Veröffentlichungsdatum des Beitrags geändert hat, müsst ihr es mit new Date(wp.data.select( 'core/editor' ).getEditedPostAttribute( 'date' )) parsen. Für den initialen Zustand solltet ihr das parsen auf jeden Fall außerhalb der Funktion erledigen, damit ihr nur den aktuellen Zustand und nicht beide in der Funktion parsen müsst.

Debounce

Eine weitere Möglichkeit, die Performance nicht zu gefährden, ist sicherzustellen, dass eure Funktion maximal jede Sekunde (oder 2, 5, 10…) ausgeführt wird. Das geht mit der debounce-Funktion von lodash, welche WordPress im Block Editor bereitstellt:

wp.data.subscribe(lodash.debounce(function () { }, 1000))

Das Limit wird in Millisekunden (hier 1000 Millisekunden = 1 Sekunde) angegeben.

Fazit

Die Möglichkeit sich bei jeder Änderung am State benachrichtigen zu lassen, ist ein sehr mächtiges Werkzeug im Block Editor für Entwickler. Man muss damit aber umgehen können, sonst hat man sehr schnell ein großes Performanceproblem.

Setzt ihr bereits die subscribe() ein? Welche Erfahrungen habt ihr damit gemacht? Habt ihr noch Tipps, die ich bisher nicht kenne? Immer her damit!

Schreibe einen Kommentar

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