ディレクティブとは何ですか?

ディレクティブは、ブロックのマークアップに追加されるカスタム属性で、DOM要素に動作を追加します。これは、render.phpファイル(動的ブロック用)またはsave.jsファイル(静的ブロック用)で行うことができます。

インタラクティビティAPIのディレクティブは、data-プレフィックスを使用します。以下は、HTMLマークアップで使用されるディレクティブの例です。

  1. <div
  2. data-wp-interactive="myPlugin"
  3. data-wp-context='{ "isOpen": false }'
  4. data-wp-watch="callbacks.logIsOpen"
  5. >
  6. <button
  7. data-wp-on--click="actions.toggle"
  8. data-wp-bind--aria-expanded="context.isOpen"
  9. aria-controls="p-1"
  10. >
  11. Toggle
  12. </button>
  13. <p id="p-1" data-wp-bind--hidden="!context.isOpen">
  14. This element is now visible!
  15. </p>
  16. </div>

ディレクティブは、HTMLタグプロセッサを使用して動的に注入することもできます。

ディレクティブを使用すると、副作用、状態、イベントハンドラ、属性、またはコンテンツなどのインタラクションを直接管理できます。

ディレクティブのリスト

wp-interactive

  1. ``````bash
  2. <!-- Let's make this element and its children interactive and set the namespace -->
  3. <div
  4. data-wp-interactive="myPlugin"
  5. data-wp-context='{ "myColor" : "red", "myBgColor": "yellow" }'
  6. >
  7. <p>I'm interactive now, <span data-wp-style--background-color="context.myBgColor">and I can use directives!</span></p>
  8. <div>
  9. <p>I'm also interactive, <span data-wp-style--color="context.myColor">and I can also use directives!</span></p>
  10. </div>
  11. </div>
  12. <!-- This is also valid -->
  13. <div
  14. data-wp-interactive='{ "namespace": "myPlugin" }'
  15. data-wp-context='{ "myColor" : "red", "myBgColor": "yellow" }'
  16. >
  17. <p>I'm interactive now, <span data-wp-style--background-color="context.myBgColor">and I can use directives!</span></p>
  18. <div>
  19. <p>I'm also interactive, <span data-wp-style--color="context.myColor">and I can also use directives!</span></p>
  20. </div>
  21. </div>
  22. `
  1. <a name="wp-context"></a>
  2. ### wp-context
  3. 特定のHTMLノードとその子要素に利用可能な**ローカル**状態を提供します。
  4. `````wp-context`````ディレクティブは、値として文字列化されたJSONを受け入れます。
  5. ``````bash
  6. // render.php
  7. <div data-wp-context='{ "post": { "id": <?php echo $post->ID; ?> } }' >
  8. <button data-wp-on--click="actions.logId" >
  9. Click Me!
  10. </button>
  11. </div>
  12. `

上記のディレクティブで使用されるストアを参照してください

  1. store( "myPlugin", {
  2. actions: {
  3. logId: () => {
  4. const { post } = getContext();
  5. console.log( post.id );
  6. },
  7. },
  8. } );

異なるコンテキストは異なるレベルで定義でき、深いレベルは親のコンテキストと自分のコンテキストをマージします:

  1. <div data-wp-context='{ "foo": "bar" }'>
  2. <span data-wp-text="context.foo"><!-- Will output: "bar" --></span>
  3. <div data-wp-context='{ "bar": "baz" }'>
  4. <span data-wp-text="context.foo"><!-- Will output: "bar" --></span>
  5. <div data-wp-context='{ "foo": "bob" }'>
  6. <span data-wp-text="context.foo"><!-- Will output: "bob" --></span>
  7. </div>
  8. </div>
  9. </div>

wp-bind

このディレクティブは、真偽値または文字列値に基づいて要素のHTML属性を設定することを許可します。構文はdata-wp-bind--attributeに従います。

  1. <li data-wp-context='{ "isMenuOpen": false }'>
  2. <button
  3. data-wp-on--click="actions.toggleMenu"
  4. data-wp-bind--aria-expanded="context.isMenuOpen"
  5. >
  6. Toggle
  7. </button>
  8. <div data-wp-bind--hidden="!context.isMenuOpen">
  9. <span>Title</span>
  10. <ul>
  11. SUBMENU ITEMS
  12. </ul>
  13. </div>
  14. </li>

上記のディレクティブで使用されるストアを参照してください

  1. store( "myPlugin", {
  2. actions: {
  3. toggleMenu: () => {
  4. const context = getContext();
  5. context.isMenuOpen = !context.isMenuOpen;
  6. },
  7. },
  8. } );
  1. - 要素が作成されたとき
  2. - 最終的なディレクティブの値を取得するために関与する`````state`````または`````context`````のプロパティのいずれかに変更があるたびに(コールバック内または参照として渡された式内)
  3. `````wp-bind`````ディレクティブが最終値を取得するためにコールバックを参照する場合:
  4. - `````wp-bind`````ディレクティブは、コールバック内で使用される`````state`````または`````context`````のプロパティのいずれかに変更があるたびに実行されます。
  5. - コールバック関数で返された値は、関連する属性の値を変更するために使用されます。
  6. `````wp-bind`````は、DOM要素が適用されるときに、その値に応じて異なる動作をします:
  7. - 値が`````true`````の場合、属性が追加されます:`````<div attribute>
  • 値がfalseの場合、属性が削除されます:<div>
  • 値が文字列の場合、属性がその値で追加されます:<div attribute="value"
  • 属性名がaria-またはdata-で始まり、値が真偽値(trueまたはfalse)の場合、属性はDOMに追加され、真偽値が文字列として割り当てられます:<div aria-attribute="true">

wp-class

このディレクティブは、真偽値に応じてHTML要素にクラスを追加または削除します。構文はdata-wp-class--classnameに従います。

  1. <div>
  2. <li
  3. data-wp-context='{ "isSelected": false }'
  4. data-wp-on--click="actions.toggleSelection"
  5. data-wp-class--selected="context.isSelected"
  6. >
  7. Option 1
  8. </li>
  9. <li
  10. data-wp-context='{ "isSelected": false }'
  11. data-wp-on--click="actions.toggleSelection"
  12. data-wp-class--selected="context.isSelected"
  13. >
  14. Option 2
  15. </li>
  16. </div>

上記のディレクティブで使用されるストアを参照してください

  1. store( "myPlugin", {
  2. actions: {
  3. toggleSelection: () => {
  4. const context = getContext();
  5. context.isSelected = !context.isSelected
  6. }
  7. }
  8. } );
  1. - 要素が作成されたとき
  2. - 最終的なディレクティブの値を取得するために関与する`````state`````または`````context`````のプロパティのいずれかに変更があるたびに(コールバック内または参照として渡された式内)
  3. ディレクティブによって受け取られた真偽値は、`````true`````のときに関連するクラス名をトグル(追加または削除)するために使用されます。
  4. `````wp-class`````ディレクティブを使用する際は、クラス名にはキャメルケースではなくケバブケースを使用することをお勧めします。これは、HTML属性が大文字と小文字を区別しないため、HTML`````data-wp-class--isDark``````````data-wp-class--isdark``````````DATA-WP-CLASS--ISDARK`````と同じように扱うからです。
  5. したがって、たとえば、クラス名`````is-dark``````````isDark`````の代わりに、`````data-wp-class--is-dark``````````data-wp-class--isDark`````の代わりに使用してください:
  6. ``````bash
  7. <!-- Recommended -->
  8. <div data-wp-class--is-dark="context.isDarkMode">
  9. <!-- ... -->
  10. </div>
  11. <!-- Not recommended -->
  12. <div data-wp-class--isDark="context.isDarkMode">
  13. <!-- ... -->
  14. </div>
  15. `
  1. /* Recommended */
  2. .is-dark {
  3. /* ... */
  4. }
  5. /* Not recommended */
  6. .isDark {
  7. /* ... */
  8. }

wp-style

このディレクティブは、値に応じてHTML要素にインラインスタイルを追加または削除します。構文はdata-wp-style--css-propertyに従います。

  1. <div data-wp-context='{ "color": "red" }' >
  2. <button data-wp-on--click="actions.toggleContextColor">Toggle Color Text</button>
  3. <p data-wp-style--color="context.color">Hello World!</p>
  4. </div>
  5. >

上記のディレクティブで使用されるストアを参照してください

  1. store( "myPlugin", {
  2. actions: {
  3. toggleContextColor: () => {
  4. const context = getContext();
  5. context.color = context.color === 'red' ? 'blue' : 'red';
  6. },
  7. },
  8. } );
  1. - 要素が作成されたとき
  2. - 最終的なディレクティブの値を取得するために関与する`````state`````または`````context`````のプロパティのいずれかに変更があるたびに(コールバック内または参照として渡された式内)
  3. ディレクティブによって受け取られた値は、関連するCSSプロパティを持つスタイル属性を追加または削除するために使用されます:
  4. - 値が`````false`````の場合、スタイル属性が削除されます:`````<div>
  • 値が文字列の場合、属性がその値で追加されます:<div style="css-property: value;">

wp-text

HTML要素の内部テキストを設定します。

  1. <div data-wp-context='{ "text": "Text 1" }'>
  2. <span data-wp-text="context.text"></span>
  3. <button data-wp-on--click="actions.toggleContextText">
  4. Toggle Context Text
  5. </button>
  6. </div>

上記のディレクティブで使用されるストアを参照してください

  1. store( "myPlugin", {
  2. actions: {
  3. toggleContextText: () => {
  4. const context = getContext();
  5. context.text = context.text === 'Text 1' ? 'Text 2' : 'Text 1';
  6. },
  7. },
  8. } );
  1. - 要素が作成されたとき
  2. - 最終的なディレクティブの値を取得するために関与する`````state`````または`````context`````のプロパティのいずれかに変更があるたびに(コールバック内または参照として渡された式内)
  3. 返された値は、要素の内部コンテンツを変更するために使用されます:`````<div>value</div>`````
  4. <a name="wp-on"></a>
  5. ### wp-on
  6. ディレクティブコードがイベントオブジェクトへの同期アクセスを必要としない場合は、よりパフォーマンスの良い[`````wp-on-async`````](#wp-on-async)を使用することを検討してください。同期アクセスが必要な場合は、同期APIを呼び出した後にメインスレッドに戻る[`````async action`````](#async-actions)を実装することを検討してください。
  7. このディレクティブは、`````click``````````keyup`````のようなディスパッチされたDOMイベントでコードを実行します。構文は`````data-wp-on--[event]``````````data-wp-on--click``````````data-wp-on--keyup`````のように)です。
  8. ``````bash
  9. <button data-wp-on--click="actions.logTime" >
  10. Click Me!
  11. </button>
  12. `

上記のディレクティブで使用されるストアを参照してください

  1. store( "myPlugin", {
  2. actions: {
  3. logTime: ( event ) => {
  4. console.log( new Date() )
  5. },
  6. },
  7. } );
  1. 参照として渡されたコールバックは[イベント](https://developer.mozilla.org/en-US/docs/Web/API/Event)(`````event`````)を受け取り、このコールバックによって返された値は無視されます。
  2. <a name="wp-on-async"></a>
  3. ### wp-on-async
  4. このディレクティブは、`````wp-on`````のよりパフォーマンスの良いアプローチです。長いタスクに寄与しないようにすぐにメインに戻り、メインスレッドで待機している他のインタラクションが早く実行できるようにします。`````event`````オブジェクトへの同期アクセスが必要ない場合は、この非同期バージョンを使用してください。特に、`````event.preventDefault()``````````event.stopPropagation()``````````event.stopImmediatePropagation()`````のメソッドに関してです。
  5. <a name="wp-on-window"></a>
  6. ### wp-on-window
  7. ディレクティブコードがイベントオブジェクトへの同期アクセスを必要としない場合は、よりパフォーマンスの良い[`````wp-on-async-window`````](#wp-on-async-window)を使用することを検討してください。同期アクセスが必要な場合は、同期APIを呼び出した後にメインスレッドに戻る[`````async action`````](#async-actions)を実装することを検討してください。
  8. このディレクティブを使用すると、`````resize``````````copy``````````focus`````のようなグローバルウィンドウイベントを添付し、それらが発生したときに定義されたコールバックを実行できます。
  9. [サポートされているウィンドウイベントのリスト。](https://developer.mozilla.org/en-US/docs/Web/API/Window#events)
  10. このディレクティブの構文は`````data-wp-on-window--[window-event]``````````data-wp-on-window--resize``````````data-wp-on-window--languagechange`````のように)です。
  11. ``````bash
  12. <div data-wp-on-window--resize="callbacks.logWidth"></div>
  13. `

上記のディレクティブで使用されるストアを参照してください

  1. store( "myPlugin", {
  2. callbacks: {
  3. logWidth() {
  4. console.log( 'Window width: ', window.innerWidth );
  5. },
  6. },
  7. } );

参照として渡されたコールバックはイベントevent)を受け取り、このコールバックによって返された値は無視されます。要素がDOMから削除されると、イベントリスナーも削除されます。

wp-on-async-window

  1. <a name="wp-on-document"></a>
  2. ### wp-on-document
  3. ディレクティブコードがイベントオブジェクトへの同期アクセスを必要としない場合は、よりパフォーマンスの良い[`````wp-on-async-document`````](#wp-on-async-document)を使用することを検討してください。同期アクセスが必要な場合は、同期APIを呼び出した後にメインスレッドに戻る[`````async action`````](#async-actions)を実装することを検討してください。
  4. このディレクティブを使用すると、`````scroll`````、`````mousemove`````、`````keydown`````のようなグローバルドキュメントイベントを添付し、それらが発生したときに定義されたコールバックを実行できます。
  5. [サポートされているドキュメントイベントのリスト。](https://developer.mozilla.org/en-US/docs/Web/API/Document#events)
  6. このディレクティブの構文は`````data-wp-on-document--[document-event]`````(`````data-wp-on-document--keydown`````や`````data-wp-on-document--selectionchange`````のように)です。
  7. ``````bash
  8. <div data-wp-on-document--keydown="callbacks.logKeydown"></div>
  9. `

上記のディレクティブで使用されるストアを参照してください

  1. store( "myPlugin", {
  2. callbacks: {
  3. logKeydown(event) {
  4. console.log( 'Key pressed: ', event.key );
  5. },
  6. },
  7. } );

参照として渡されたコールバックはイベントevent)を受け取り、このコールバックによって返された値は無視されます。要素がDOMから削除されると、イベントリスナーも削除されます。

wp-on-async-document

  1. <a name="wp-watch"></a>
  2. ### wp-watch
  3. ノードが作成されたときにコールバックを実行し、状態またはコンテキストが変更されたときに再度実行します。
  4. `````data-wp-watch--[unique-id]`````の構文を使用して、同じDOM要素に複数の副作用を添付できます。
  5. `````unique-id`````は、グローバルに一意である必要はありません。単に、そのDOM要素の`````wp-watch`````ディレクティブの他の一意のIDと異なる必要があります。
  6. ``````bash
  7. <div
  8. data-wp-context='{ "counter": 0 }'
  9. data-wp-watch="callbacks.logCounter"
  10. >
  11. <p>Counter: <span data-wp-text="context.counter"></span></p>
  12. <button data-wp-on--click="actions.increaseCounter">+</button>
  13. <button data-wp-on--click="actions.decreaseCounter">-</button>
  14. </div>
  15. `

上記のディレクティブで使用されるストアを参照してください

  1. store( "myPlugin", {
  2. actions: {
  3. increaseCounter: () => {
  4. const context = getContext();
  5. context.counter++;
  6. },
  7. decreaseCounter: () => {
  8. const context = getContext();
  9. context.counter--;
  10. },
  11. },
  12. callbacks: {
  13. logCounter: () => {
  14. const { counter } = getContext();
  15. console.log("Counter is " + counter + " at " + new Date() );
  16. },
  17. },
  18. } );
  1. - 要素が作成されたとき
  2. - コールバック内で使用される`````state`````または`````context`````のプロパティのいずれかが変更されるたびに
  3. `````wp-watch`````ディレクティブは関数を返すことがあります。返された場合、その関数はクリーンアップロジックとして使用されます。つまり、コールバックが再度実行される直前に実行され、要素がDOMから削除されるときにも実行されます。
  4. このディレクティブのいくつかの使用例は次のとおりです:
  5. - ロギング
  6. - ページのタイトルを変更する
  7. - `````.focus()`````を持つ要素にフォーカスを設定する。
  8. - 特定の条件が満たされたときに状態またはコンテキストを変更する
  9. <a name="wp-init"></a>
  10. ### wp-init
  11. このディレクティブは、ノードが作成されたときにのみコールバックを実行します。
  12. `````wp-init`````の構文を使用して、同じDOM要素に複数の`````data-wp-init--[unique-id]`````を添付できます。
  13. `````unique-id`````は、グローバルに一意である必要はありません。単に、そのDOM要素の`````wp-init`````ディレクティブの他の一意のIDと異なる必要があります。
  14. ``````bash
  15. <div data-wp-init="callbacks.logTimeInit">
  16. <p>Hi!</p>
  17. </div>
  18. `

同じDOM要素に複数のwp-initディレクティブがある別の例を示します。

  1. <form
  2. data-wp-init--log="callbacks.logTimeInit"
  3. data-wp-init--focus="callbacks.focusFirstElement"
  4. >
  5. <input type="text">
  6. </form>

上記のディレクティブで使用されるストアを参照してください

  1. import { store, getElement } from '@wordpress/interactivity';
  2. store( "myPlugin", {
  3. callbacks: {
  4. logTimeInit: () => console.log( `Init at ` + new Date() ),
  5. focusFirstElement: () => {
  6. const { ref } = getElement();
  7. ref.querySelector( 'input:first-child' ).focus(),
  8. },
  9. },
  10. } );
  1. <a name="wp-run"></a>
  2. ### wp-run
  3. このディレクティブは、ノードのレンダリング実行中に渡されたコールバックを実行します。
  4. `````useState`````、`````useWatch`````、または`````useEffect`````のようなフックを渡されたコールバック内で使用および構成し、以前のディレクティブよりも柔軟性のある独自のロジックを作成できます。
  5. `````wp-run`````の構文を使用して、同じDOM要素に複数の`````data-wp-run--[unique-id]`````を添付できます。
  6. `````unique-id`````は、グローバルに一意である必要はありません。単に、そのDOM要素の`````wp-run`````ディレクティブの他の一意のIDと異なる必要があります。
  7. ``````bash
  8. <div data-wp-run="callbacks.logInView">
  9. <p>Hi!</p>
  10. </div>
  11. `

上記のディレクティブで使用されるストアを参照してください

  1. import { getElement, store, useState, useEffect } from '@wordpress/interactivity';
  2. // Unlike `data-wp-init` and `data-wp-watch`, you can use any hooks inside
  3. // `data-wp-run` callbacks.
  4. const useInView = () => {
  5. const [ inView, setInView ] = useState( false );
  6. useEffect( () => {
  7. const { ref } = getElement();
  8. const observer = new IntersectionObserver( ( [ entry ] ) => {
  9. setInView( entry.isIntersecting );
  10. } );
  11. observer.observe( ref );
  12. return () => ref && observer.unobserve( ref );
  13. }, []);
  14. return inView;
  15. };
  16. store( 'myPlugin', {
  17. callbacks: {
  18. logInView: () => {
  19. const isInView = useInView();
  20. useEffect( () => {
  21. if ( isInView ) {
  22. console.log( 'Inside' );
  23. } else {
  24. console.log( 'Outside' );
  25. }
  26. });
  27. }
  28. },
  29. } );

(P)Reactコンポーネントと同様に、refは最初のレンダリング中にgetElement()からnullです。DOM要素の参照に適切にアクセスするには、通常、useEffectuseInit、またはuseWatchのようなエフェクトのようなフックを使用する必要があります。これにより、getElement()がコンポーネントがマウントされた後に実行され、refが利用可能になります。

wp-key

  1. キーは、兄弟要素の中で要素を一意に識別する文字列である必要があります。通常、リスト項目のような繰り返し要素に使用されます。例えば:
  2. ``````bash
  3. <ul>
  4. <li data-wp-key="unique-id-1">Item 1</li>
  5. <li data-wp-key="unique-id-2">Item 2</li>
  6. </ul>
  7. `

しかし、他の要素にも使用できます:

  1. <div>
  2. <a data-wp-key="previous-page" ...>Previous page</a>
  3. <a data-wp-key="next-page" ...>Next page</a>
  4. </div>

リストが再レンダリングされると、インタラクティビティAPIはキーによって要素を一致させ、アイテムが追加/削除/再配置されたかどうかを判断します。キーのない要素は不必要に再作成される可能性があります。

wp-each

  1. 各アイテムは、デフォルトで`````item`````という名前でコンテキストに含まれ、テンプレート内のディレクティブは現在のアイテムにアクセスできます。
  2. 例えば、次のHTMLを考えてみましょう。
  3. ``````bash
  4. <ul data-wp-context='{ "list": [ "hello", "hola", "olá" ] }'>
  5. <template data-wp-each="context.list" >
  6. <li data-wp-text="context.item"></li>
  7. </template>
  8. </ul>
  9. `

次の出力が生成されます:

  1. <ul data-wp-context='{ "list": [ "hello", "hola", "olá" ] }'>
  2. <li data-wp-text="context.item">hello</li>
  3. <li data-wp-text="context.item">hola</li>
  4. <li data-wp-text="context.item">olá</li>
  5. </ul>

コンテキスト内のアイテムを保持するプロパティは、ディレクティブ名にサフィックスを渡すことで変更できます。次の例では、デフォルトのプロパティがitemからgreetingに変更されます。

  1. <ul data-wp-context='{ "list": [ "hello", "hola", "olá" ] }'>
  2. <template data-wp-each--greeting="context.list" >
  3. <li data-wp-text="context.greeting"></li>
  4. </template>
  5. </ul>

デフォルトでは、レンダリングされたノードのキーとして各要素を使用しますが、必要に応じてキーを取得するためのパスを指定することもできます。たとえば、リストがオブジェクトを含む場合です。

そのためには、data-wp-each-key<template>タグで使用し、テンプレートコンテンツ内ではdata-wp-keyを使用しない必要があります。これは、data-wp-eachが各レンダリングされたアイテムの周りにコンテキストプロバイダーラッパーを作成し、それらのラッパーがkeyプロパティを必要とするからです。

  1. <ul data-wp-context='{
  2. "list": [
  3. { "id": "en", "value": "hello" },
  4. { "id": "es", "value": "hola" },
  5. { "id": "pt", "value": "olá" }
  6. ]
  7. }'>
  8. <template
  9. data-wp-each--greeting="context.list"
  10. data-wp-each-key="context.greeting.id"
  11. >
  12. <li data-wp-text="context.greeting.value"></li>
  13. </template>
  14. </ul>

wp-each-child

サーバーサイドでレンダリングされたリストの場合、data-wp-each-childという別のディレクティブが、ハイドレーションが期待通りに機能することを保証します。このディレクティブは、サーバーでディレクティブが処理されるときに自動的に追加されます。

  1. <ul data-wp-context='{ "list": [ "hello", "hola", "olá" ] }'>
  2. <template data-wp-each--greeting="context.list" >
  3. <li data-wp-text="context.greeting"></li>
  4. </template>
  5. <li data-wp-each-child>hello</li>
  6. <li data-wp-each-child>hola</li>
  7. <li data-wp-each-child>olá</li>
  8. </ul>

ディレクティブの値はストアプロパティへの参照です

ディレクティブに割り当てられた値は、特定の状態、アクション、または副作用を指す文字列です。

次の例では、ゲッターを使用してstate.isPlaying派生値を定義します。

  1. const { state } = store( "myPlugin", {
  2. state: {
  3. currentVideo: '',
  4. get isPlaying() {
  5. return state.currentVideo !== '';
  6. }
  7. },
  8. } );

そして、文字列値"state.isPlaying"がこのセレクタの結果をdata-wp-bind--hiddenに割り当てるために使用されます。

  1. <div data-wp-bind--hidden="!state.isPlaying" ... >
  2. <iframe ...></iframe>
  3. </div>

これらのディレクティブに割り当てられた値は、ストア内の特定のプロパティへの参照です。これらは自動的にディレクティブに接続され、各ディレクティブは「どのストア要素を参照しているか」を知ることができます。追加の設定は必要ありません。

デフォルトでは、参照は現在の名前空間内のプロパティを指します。これは、data-wp-interactive属性を持つ最も近い祖先によって指定されたものです。異なる名前空間のプロパティにアクセスする必要がある場合は、アクセスされるプロパティが定義されている名前空間を明示的に設定できます。構文はnamespace::referenceで、namespaceを適切な値に置き換えます。

以下の例では、state.isPlayingotherPluginから取得されていますが、myPluginからではありません:

  1. <div data-wp-interactive="myPlugin">
  2. <div data-wp-bind--hidden="otherPlugin::!state.isPlaying" ... >
  3. <iframe ...></iframe>
  4. </div>
  5. </div>

ストア

ストアは、ディレクティブにリンクされたロジック(アクション、副作用など)と、そのロジック内で使用されるデータ(状態、派生状態など)を作成するために使用されます。

ストアは通常、各ブロックのview.jsファイルで作成されますが、状態はブロックのrender.phpから初期化できます。

ストアの要素

状態

ページのHTMLノードに利用可能なデータを定義します。データを定義する2つの方法を区別することが重要です:

  • グローバル状態store()関数を使用してstateプロパティで定義され、データはページのすべてのHTMLノードに利用可能です。
  • コンテキスト/ローカル状態:HTMLノード内でdata-wp-contextディレクティブを使用して定義され、データはそのHTMLノードとその子要素に利用可能です。アクション、派生状態、または副作用内でgetContext関数を使用してアクセスできます。
  1. <div data-wp-context='{ "someText": "Hello World!" }'>
  2. <!-- Access global state -->
  3. <span data-wp-text="state.someText"></span>
  4. <!-- Access local state (context) -->
  5. <span data-wp-text="context.someText"></span>
  6. </div>
  1. const { state } = store( "myPlugin", {
  2. state: {
  3. someText: "Hello Universe!"
  4. },
  5. actions: {
  6. someAction: () => {
  7. state.someText // Access or modify global state - "Hello Universe!"
  8. const context = getContext();
  9. context.someText // Access or modify local state (context) - "Hello World!"
  10. },
  11. },
  12. } )

アクション

アクションは通常のJavaScript関数です。通常、data-wp-onディレクティブ(イベントリスナーを使用)または他のアクションによってトリガーされます。

  1. const { state, actions } = store("myPlugin", {
  2. actions: {
  3. selectItem: ( id ) => {
  4. const context = getContext();
  5. // `id` is optional here, so this action can be used in a directive.
  6. state.selected = id || context.id;
  7. },
  8. otherAction: () => {
  9. // but it can also be called from other actions.
  10. actions.selectItem(123); // it works and type is correct
  11. }
  12. }
  13. });

非同期アクション

非同期アクションは、async/awaitの代わりにジェネレーターを使用する必要があります。

非同期関数では、制御は関数自体に渡されます。関数の呼び出し元は、関数が待機しているかどうか、そしてより重要なことに、awaitが解決され、関数が実行を再開したかどうかを知る方法がありません。この情報が必要です。スコープを復元するために。

2つのボタンを持つブロックを想像してください。一方はisOpen: trueを持つコンテキスト内にあり、もう一方はisOpen: falseを持っています:

  1. <div data-wp-context='{ "isOpen": true }'>
  2. <button data-wp-on--click="actions.someAction">Click</button>
  3. </div>
  4. <div data-wp-context='{ "isOpen": false }'>
  5. <button data-wp-on--click="actions.someAction">Click</button>
  6. </div>

アクションが非同期で長い遅延を待機する必要がある場合。

  • ユーザーが最初のボタンをクリックします。
  • スコープは最初のコンテキストを指します。isOpen: true
  • 最初のstate.isOpenへのアクセスは正しいです。getContextが現在のスコープを返すからです。
  • アクションは長い遅延を待機し始めます。
  • アクションが再開される前に、ユーザーが2番目のボタンをクリックします。
  • スコープは2番目のコンテキストに変更されます。isOpen: false
  • 最初のstate.isOpenへのアクセスは正しいです。getContextが現在のスコープを返すからです。
  • 2番目のアクションが長い遅延を待機し始めます。
  • 最初のアクションが待機を終了し、実行を再開します。
  • 最初のアクションの2回目のstate.isOpenへのアクセスは不正確です。getContextが今や間違ったスコープを返すからです。

非同期アクションが待機を開始し、操作を再開するタイミングを知る必要があります。これがジェネレーターの役割です。

ストアは次のように書かれている場合、正常に機能します:

  1. const { state } = store("myPlugin", {
  2. state: {
  3. get isOpen() {
  4. return getContext().isOpen;
  5. },
  6. },
  7. actions: {
  8. someAction: function* () {
  9. state.isOpen; // This context is correct because it's synchronous.
  10. yield longDelay(); // With generators, the caller controls when to resume this function.
  11. state.isOpen; // This context is correct because we restored the proper scope before we resumed the function.
  12. },
  13. },
  14. });

上記のwp-onwp-on-windowwp-on-documentで述べたように、非同期アクションは、アクションがasyncオブジェクトへの同期アクセスを必要とするため、前述のディレクティブのasyncバージョンを使用できない場合に使用する必要があります。アクションがevent.preventDefault()event.stopPropagation()、またはevent.stopImmediatePropagation()を呼び出す必要がある場合は、同期アクセスが必要です。アクションコードが長いタスクに寄与しないようにするために、同期イベントAPIを呼び出した後に手動でメインスレッドに戻ることができます。例えば:

  1. // Note: In WordPress 6.6 this splitTask function is exported by @wordpress/interactivity.
  2. function splitTask() {
  3. return new Promise(resolve => {
  4. setTimeout(resolve, 0);
  5. });
  6. }
  7. store("myPlugin", {
  8. actions: {
  9. handleClick: function* (event) {
  10. event.preventDefault();
  11. yield splitTask();
  12. doTheWork();
  13. },
  14. },
  15. });

多くの作業を行っている場合は、アクション内にこのようなyieldポイントを複数追加することをお勧めします。

副作用

状態の変更に自動的に反応します。通常、data-wp-watchまたはdata-wp-initディレクティブによってトリガーされます。

派生状態

状態の計算されたバージョンを返します。statecontextの両方にアクセスできます。

  1. // view.js
  2. const { state } = store( "myPlugin", {
  3. state: {
  4. amount: 34,
  5. defaultCurrency: 'EUR',
  6. currencyExchange: {
  7. USD: 1.1,
  8. GBP: 0.85,
  9. },
  10. get amountInUSD() {
  11. return state.currencyExchange[ 'USD' ] * state.amount;
  12. },
  13. get amountInGBP() {
  14. return state.currencyExchange[ 'GBP' ] * state.amount;
  15. },
  16. },
  17. } );

コールバック内でのデータへのアクセス

store は、stateactions、またはcallbacksのようなすべてのストアプロパティを含みます。これらはstore()呼び出しによって返されるため、分割代入を使用してアクセスできます:

  1. const { state, actions } = store( "myPlugin", {
  2. // ...
  3. } );
  1. ``````bash
  2. store( "myPlugin", {
  3. state: {
  4. someValue: 1,
  5. }
  6. } );
  7. const { state } = store( "myPlugin", {
  8. actions: {
  9. someAction() {
  10. state.someValue // = 1
  11. }
  12. }
  13. } );
  14. `

すべてのstore()呼び出しは同じ名前空間で同じ参照を返します。つまり、stateactionsなど、すべてのストア部分をマージした結果を含みます。

  • アクション、派生状態、または副作用内でコンテキストにアクセスするには、getContext関数を使用できます。
  • 参照にアクセスするには、getElement関数を使用できます。
  1. const { state } = store( "myPlugin", {
  2. state: {
  3. get someDerivedValue() {
  4. const context = getContext();
  5. const { ref } = getElement();
  6. // ...
  7. }
  8. },
  9. actions: {
  10. someAction() {
  11. const context = getContext();
  12. const { ref } = getElement();
  13. // ...
  14. }
  15. },
  16. callbacks: {
  17. someEffect() {
  18. const context = getContext();
  19. const { ref } = getElement();
  20. // ...
  21. }
  22. }
  23. } );

このアプローチにより、ディレクティブを柔軟で強力にするいくつかの機能が可能になります:

  • アクションと副作用は、状態とコンテキストを読み取り、変更できます。
  • ブロック内のアクションと状態は、他のブロックからアクセスできます。
  • アクションと副作用は、通常のJavaScript関数ができることは何でもできます。たとえば、DOMにアクセスしたり、APIリクエストを行ったりできます。
  • 副作用は状態の変更に自動的に反応します。

ストアの設定

クライアント側で

各ブロックのview.jsファイル内で 開発者は、アクション、副作用、または派生状態のような関数を参照して、状態とストアの要素の両方を定義できます。

ストアをJavaScriptで設定するために使用されるstoreメソッドは、@wordpress/interactivityからインポートできます。

  1. // store
  2. import { store, getContext } from '@wordpress/interactivity';
  3. store( "myPlugin", {
  4. actions: {
  5. toggle: () => {
  6. const context = getContext();
  7. context.isOpen = !context.isOpen;
  8. },
  9. },
  10. callbacks: {
  11. logIsOpen: () => {
  12. const { isOpen } = getContext();
  13. // Log the value of `isOpen` each time it changes.
  14. console.log( `Is open: ${ isOpen }` );
  15. }
  16. },
  17. });

サーバー側で

状態は、wp_interactivity_state()関数を使用してサーバーで初期化することもできます。通常、ブロックのrender.phpファイルでこれを行います(render.phpテンプレートはWordPress 6.1で導入されました)。

  1. `````wp_interactivity_state`````関数は、参照として使用される名前空間を持つ`````string`````と、値を含む[連想配列](https://www.php.net/manual/en/language.types.array.php)の2つの引数を受け取ります。
  2. *`````state````` = `````{ someValue: 123 }`````でサーバーから初期化されたストアの例*
  3. ``````bash
  4. // render.php
  5. wp_interactivity_state( 'myPlugin', array (
  6. 'someValue' => get_some_value()
  7. ));
  8. `

サーバーで状態を初期化すると、任意のWordPress APIを使用することもできます。たとえば、コア翻訳APIを使用して状態の一部を翻訳できます:

  1. // render.php
  2. wp_interactivity_state( 'favoriteMovies', array(
  3. "1" => array(
  4. "id" => "123-abc",
  5. "movieName" => __("someMovieName", "textdomain")
  6. ),
  7. ) );

プライベートストア

特定のストア名前空間をプライベートとしてマークすることができ、他の名前空間からその内容にアクセスできないようにします。そのメカニズムは、lockオプションをstore()呼び出しに追加することによって行われます。以下の例のように。このようにして、同じロックされた名前空間でのstore()のさらなる実行はエラーをスローし、その名前空間は最初のstore()呼び出しから返された場所でのみアクセスできることを意味します。これは、プラグインストアの一部を隠したい開発者に特に便利です。

  1. const { state } = store(
  2. "myPlugin/private",
  3. { state: { messages: [ "private message" ] } },
  4. { lock: true }
  5. );
  6. // The following call throws an Error!
  7. store( "myPlugin/private", { /* store part */ } );

プライベートストアを解除する方法もあります。ブール値を渡す代わりに、lock値として文字列を使用できます。そのような文字列は、同じ名前空間への後続のstore()呼び出しでその内容を解除するために使用できます。文字列ロックを知っているコードのみが、保護されたストア名を解除できます。これは、複数のJSモジュールで定義された複雑なストアに便利です。

  1. const { state } = store(
  2. "myPlugin/private",
  3. { state: { messages: [ "private message" ] } },
  4. { lock: PRIVATE_LOCK }
  5. );
  6. // The following call works as expected.
  7. store( "myPlugin/private", { /* store part */ }, { lock: PRIVATE_LOCK } );

ストアクライアントメソッド

ストア関数の他に、開発者がストア関数のデータにアクセスできるいくつかのメソッドもあります。

  • getContext()
  • getElement()

getContext()

ストアから関数を評価している要素によって継承されたコンテキストを取得します。返される値は、要素と関数がgetContext()を呼び出している名前空間によって異なります。特定のインタラクティブ領域のコンテキストを取得するために、オプションの名前空間引数を取ることもできます。

  1. const context = getContext('namespace');
  • namespace(オプション):インタラクティブ領域の名前空間に一致する文字列。提供されない場合は、現在のインタラクティブ領域のコンテキストを取得します。
  1. // render.php
  2. <div data-wp-interactive="myPlugin" data-wp-context='{ "isOpen": false }'>
  3. <button data-wp-on--click="actions.log">Log</button>
  4. </div>
  1. // store
  2. import { store, getContext } from '@wordpress/interactivity';
  3. store( "myPlugin", {
  4. actions: {
  5. log: () => {
  6. const context = getContext();
  7. // Logs "false"
  8. console.log('context => ', context.isOpen)
  9. // With namespace argument.
  10. const myPluginContext = getContext("myPlugin");
  11. // Logs "false"
  12. console.log('myPlugin isOpen => ', myPluginContext.isOpen);
  13. },
  14. },
  15. });

getElement()

アクションがバインドされているか、呼び出されている要素の表現を取得します。そのような表現は読み取り専用で、DOM要素、プロパティ、およびローカル反応状態への参照を含みます。

それは2つのキーを持つオブジェクトを返します:

ref
  1. ##### attributes
  2. `````attributes`````は、他のストア名前空間を参照できるゲッターを追加する[Proxy](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy)を含みます。コード内のゲッターを確認してください。[リンク](https://github.com/WordPress/gutenberg/blob/8cb23964d58f3ce5cf6ae1b6f967a4b8d4939a8e/packages/interactivity/src/store.ts#L70)
  3. これらの属性は、その要素のディレクティブを含みます。ボタンの例では:
  4. ``````bash
  5. // store
  6. import { store, getElement } from '@wordpress/interactivity';
  7. store( "myPlugin", {
  8. actions: {
  9. log: () => {
  10. const element = getElement();
  11. // Logs attributes
  12. console.log('element attributes => ', element.attributes)
  13. },
  14. },
  15. });
  16. `

コードは次のようにログに記録します:

  1. {
  2. "data-wp-on--click": 'actions.log',
  3. "children": ['Log'],
  4. "onclick": event => { evaluate(entry, event); }
  5. }

withScope()

アクションは、呼び出されるときにスコープに依存する場合があります。たとえば、getContext()getElement()を呼び出すときです。

インタラクティビティAPIランタイムがコールバックを実行すると、スコープは自動的に設定されます。ただし、ランタイムによって実行されないコールバックからアクションを呼び出す場合、setInterval()コールバックのように、スコープが適切に設定されていることを確認する必要があります。この場合、withScope()関数を使用してスコープが適切に設定されていることを確認してください。

  1. ``````bash
  2. store('mySliderPlugin', {
  3. callbacks: {
  4. initSlideShow: () => {
  5. setInterval(
  6. withScope( () => {
  7. actions.nextImage();
  8. } ),
  9. 3_000
  10. );
  11. },
  12. },
  13. })
  14. `

サーバー機能

インタラクティビティAPIには、サーバー上で設定オプションを初期化および参照するための便利な関数が含まれています。これは、サーバー指令処理がブラウザに送信される前にHTMLマークアップを変更するために使用する初期データを供給するために必要です。また、非同期リクエスト(AJAX)や翻訳など、WordPressの多くのAPIを活用する素晴らしい方法でもあります。

wp_interactivity_config

  1. 設定はクライアントでも利用可能ですが、静的情報です。
  2. これは、ユーザーのインタラクションに基づいて更新されないサイトのインタラクションのためのグローバル設定と考えてください。
  3. 設定の例:
  4. ``````bash
  5. wp_interactivity_config( 'myPlugin', array( 'showLikeButton' => is_user_logged_in() ) );
  6. `

取得の例:

  1. wp_interactivity_config( 'myPlugin' );

この設定はクライアントで取得できます:

  1. // view.js
  2. const { showLikeButton } = getConfig();

wp_interactivity_state

  1. サーバー上でグローバル状態を初期化することにより、[AJAX](/read/wordpress/731afd95728012d5.md)や[ノンス](c96244420fe05525.md#nonce)など、多くの重要なWordPress APIを使用することができます。
  2. `````wp_interactivity_state`````関数は、参照として使用されるネームスペースを含む文字列と、値を含む連想配列の2つの引数を受け取ります。
  3. ここに、ノンスを持つWP Admin AJAXエンドポイントを渡す例があります。
  4. ``````bash
  5. // render.php
  6. wp_interactivity_state(
  7. 'myPlugin',
  8. array(
  9. 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
  10. 'nonce' => wp_create_nonce( 'myPlugin_nonce' ),
  11. ),
  12. );
  13. `
  1. // view.js
  2. const { state } = store( 'myPlugin', {
  3. actions: {
  4. *doSomething() {
  5. try {
  6. const formData = new FormData();
  7. formData.append( 'action', 'do_something' );
  8. formData.append( '_ajax_nonce', state.nonce );
  9. const data = yield fetch( state.ajaxUrl, {
  10. method: 'POST',
  11. body: formData,
  12. } ).then( ( response ) => response.json() );
  13. console.log( 'Server data!', data );
  14. } catch ( e ) {
  15. // Something went wrong!
  16. }
  17. },
  18. },
  19. }
  20. );

wp_interactivity_process_directives

  1. これは、インタラクティビティAPIのサーバーサイドレンダリング部分のコア機能であり、公開されているため、ブロックであろうとなかろうと、任意のHTMLを処理できます。
  2. このコード
  3. ``````bash
  4. wp_interactivity_state( 'myPlugin', array( 'greeting' => 'Hello, World!' ) );
  5. $html_content = '<div data-wp-text="myPlugin::state.greeting"></div>';
  6. $processed_html = wp_interactivity_process_directives( $html_content );
  7. echo $processed_html;
  8. `

は出力します:

  1. <div data-wp-text="myPlugin::state.greeting">Hello, World!</div>

wp_interactivity_data_wp_context

  1. この関数は、サーバーサイドでレンダリングされたマークアップに`````data-wp-context`````属性を印刷するための推奨方法です。
  2. ``````bash
  3. $my_context = array(
  4. 'counter' => 0,
  5. 'isOpen' => true,
  6. );
  7. ?>
  8. <div
  9. <?php echo wp_interactivity_data_wp_context( $my_context ); ?>
  10. >
  11. </div>
  12. `

は出力します:

  1. <div data-wp-context='{"counter":0,"isOpen":true}'>