From 119caccd9130cecbd24fe62df55221af33415afb Mon Sep 17 00:00:00 2001 From: Michal Date: Mon, 21 Oct 2024 17:10:33 +0100 Subject: [PATCH] Docs: Interactivity API - Add documentation for `getServerState()` and `getServerContext()` (#66104) * Add documentation on `getServerState()` and `getServerContext()` * update heading level * Undelete the "Conclusion" * Fix link to the documentation * update link to router docs * clarify about context * add reference to @wordpress/router Co-authored-by: michalczaplinski Co-authored-by: DAreRodz --- .../interactivity-api/api-reference.md | 935 ++++++++++-------- ...l-state-local-context-and-derived-state.md | 106 +- 2 files changed, 623 insertions(+), 418 deletions(-) diff --git a/docs/reference-guides/interactivity-api/api-reference.md b/docs/reference-guides/interactivity-api/api-reference.md index 25498d10bde2ef..bbbb565684c578 100644 --- a/docs/reference-guides/interactivity-api/api-reference.md +++ b/docs/reference-guides/interactivity-api/api-reference.md @@ -6,8 +6,8 @@ Interactivity API is only available for WordPress 6.5 and above. To add interactions to blocks using the Interactivity API, developers can use: -- **Directives:** Added to the markup to add specific behavior to the DOM elements of the block -- **Store:** Contains the logic and data (state, actions, side effects, etc.) needed for the behavior +- **Directives:** Added to the markup to add specific behavior to the DOM elements of the block +- **Store:** Contains the logic and data (state, actions, side effects, etc.) needed for the behavior DOM elements are connected to data stored in the state and context through directives. If data in the state or context change directives will react to those changes, updating the DOM accordingly (see [diagram](https://excalidraw.com/#json=T4meh6lltJh6TCX51NTIu,DmIhxYSGFTL_ywZFbsmuSw)). @@ -21,21 +21,21 @@ Interactivity API directives use the `data-` prefix. Here's an example of direct ```html
- - -

- This element is now visible! -

+ + +

+ This element is now visible! +

``` @@ -52,24 +52,44 @@ The `wp-interactive` directive "activates" the interactivity for the DOM element ```html
-

I'm interactive now, and I can use directives!

-
-

I'm also interactive, and I can also use directives!

-
+

+ I'm interactive now, + and I can use directives! +

+
+

+ I'm also interactive, + and I can also use directives! +

+
-

I'm interactive now, and I can use directives!

-
-

I'm also interactive, and I can also use directives!

-
+

+ I'm interactive now, + and I can use directives! +

+
+

+ I'm also interactive, + and I can also use directives! +

+
``` @@ -96,31 +116,31 @@ The `wp-context` directive accepts a stringified JSON as a value. See store used with the directive above ```js -store( "myPlugin", { - actions: { - logId: () => { - const { post } = getContext(); - console.log( post.id ); - }, - }, +store( 'myPlugin', { + actions: { + logId: () => { + const { post } = getContext(); + console.log( post.id ); + }, + }, } ); ``` + Different contexts can be defined at different levels, and deeper levels will merge their own context with any parent one: ```html
- + -
- +
+ -
- -
- -
+
+ +
+
``` @@ -130,18 +150,18 @@ This directive allows setting HTML attributes on elements based on a boolean or ```html
  • - -
    - Title -
      - SUBMENU ITEMS -
    -
    + +
    + Title +
      + SUBMENU ITEMS +
    +
  • ``` @@ -149,33 +169,34 @@ This directive allows setting HTML attributes on elements based on a boolean or See store used with the directive above ```js -store( "myPlugin", { - actions: { - toggleMenu: () => { - const context = getContext(); - context.isMenuOpen = !context.isMenuOpen; - }, - }, +store( 'myPlugin', { + actions: { + toggleMenu: () => { + const context = getContext(); + context.isMenuOpen = ! context.isMenuOpen; + }, + }, } ); ``` + The `wp-bind` directive is executed: -- When the element is created -- Each time there's a change on any of the properties of the `state` or `context` involved in getting the final value of the directive (inside the callback or the expression passed as reference) +- When the element is created +- Each time there's a change on any of the properties of the `state` or `context` involved in getting the final value of the directive (inside the callback or the expression passed as reference) When `wp-bind` directive references a callback to get its final value: -- The `wp-bind` directive will be executed each time there's a change on any of the properties of the `state` or `context` used inside this callback. -- The returned value in the callback function is used to change the value of the associated attribute. +- The `wp-bind` directive will be executed each time there's a change on any of the properties of the `state` or `context` used inside this callback. +- The returned value in the callback function is used to change the value of the associated attribute. The `wp-bind` will do different things when the DOM element is applied, depending on its value: - - If the value is `true`, the attribute is added: `
    ` - - If the value is `false`, the attribute is removed: `
    ` - - If the value is a string, the attribute is added with its value assigned: `
    ` +- If the value is `true`, the attribute is added: `
    ` +- If the value is `false`, the attribute is removed: `
    ` +- If the value is a string, the attribute is added with its value assigned: `
    ` ### `wp-class` @@ -183,20 +204,20 @@ This directive adds or removes a class to an HTML element, depending on a boolea ```html
    -
  • - Option 1 -
  • -
  • - Option 2 -
  • +
  • + Option 1 +
  • +
  • + Option 2 +
  • ``` @@ -204,21 +225,22 @@ This directive adds or removes a class to an HTML element, depending on a boolea See store used with the directive above ```js -store( "myPlugin", { - actions: { - toggleSelection: () => { - const context = getContext(); - context.isSelected = !context.isSelected - } - } +store( 'myPlugin', { + actions: { + toggleSelection: () => { + const context = getContext(); + context.isSelected = ! context.isSelected; + }, + }, } ); ``` + The `wp-class` directive is executed: -- When the element is created -- Each time there's a change on any of the properties of the `state` or `context` involved in getting the final value of the directive (inside the callback or the expression passed as reference) +- When the element is created +- Each time there's a change on any of the properties of the `state` or `context` involved in getting the final value of the directive (inside the callback or the expression passed as reference) The boolean value received by the directive is used to toggle (add when `true` or remove when `false`) the associated class name from the `class` attribute. @@ -229,24 +251,24 @@ So, for example, use the class name `is-dark` instead of `isDark` and `data-wp-c ```html
    - +
    - +
    ``` ```css /* Recommended */ .is-dark { - /* ... */ + /* ... */ } /* Not recommended */ .isDark { - /* ... */ + /* ... */ } ``` @@ -255,9 +277,11 @@ So, for example, use the class name `is-dark` instead of `isDark` and `data-wp-c This directive adds or removes inline style to an HTML element, depending on its value. It follows the syntax `data-wp-style--css-property`. ```html -
    - -

    Hello World!

    +
    + +

    Hello World!

    > ``` @@ -266,26 +290,27 @@ This directive adds or removes inline style to an HTML element, depending on its See store used with the directive above ```js -store( "myPlugin", { - actions: { - toggleContextColor: () => { - const context = getContext(); - context.color = context.color === 'red' ? 'blue' : 'red'; - }, - }, +store( 'myPlugin', { + actions: { + toggleContextColor: () => { + const context = getContext(); + context.color = context.color === 'red' ? 'blue' : 'red'; + }, + }, } ); ``` + The `wp-style` directive is executed: -- When the element is created -- Each time there's a change on any of the properties of the `state` or `context` involved in getting the final value of the directive (inside the callback or the expression passed as reference) +- When the element is created +- Each time there's a change on any of the properties of the `state` or `context` involved in getting the final value of the directive (inside the callback or the expression passed as reference) The value received by the directive is used to add or remove the style attribute with the associated CSS property: -- If the value is `false`, the style attribute is removed: `
    ` -- If the value is a string, the attribute is added with its value assigned: `
    ` +- If the value is `false`, the style attribute is removed: `
    ` +- If the value is a string, the attribute is added with its value assigned: `
    ` ### `wp-text` @@ -293,10 +318,10 @@ It sets the inner text of an HTML element. ```html
    - - + +
    ``` @@ -304,21 +329,22 @@ It sets the inner text of an HTML element. See store used with the directive above ```js -store( "myPlugin", { - actions: { - toggleContextText: () => { - const context = getContext(); - context.text = context.text === 'Text 1' ? 'Text 2' : 'Text 1'; - }, - }, +store( 'myPlugin', { + actions: { + toggleContextText: () => { + const context = getContext(); + context.text = context.text === 'Text 1' ? 'Text 2' : 'Text 1'; + }, + }, } ); ``` + The `wp-text` directive is executed: -- When the element is created -- Each time there's a change on any of the properties of the `state` or `context` involved in getting the final value of the directive (inside the callback or the expression passed as reference) +- When the element is created +- Each time there's a change on any of the properties of the `state` or `context` involved in getting the final value of the directive (inside the callback or the expression passed as reference) The returned value is used to change the inner content of the element: `
    value
    `. @@ -340,14 +366,15 @@ This directive runs code on dispatched DOM events like `click` or `keyup`. The s See store used with the directive above ```js -store( "myPlugin", { - actions: { - logTime: ( event ) => { - console.log( new Date() ) - }, - }, +store( 'myPlugin', { + actions: { + logTime: ( event ) => { + console.log( new Date() ); + }, + }, } ); ``` + The `wp-on` directive is executed each time the associated event is triggered. @@ -378,7 +405,7 @@ The syntax of this directive is `data-wp-on-window--[window-event]` (like `data- See store used with the directive above ```js -store( "myPlugin", { +store( 'myPlugin', { callbacks: { logWidth() { console.log( 'Window width: ', window.innerWidth ); @@ -386,6 +413,7 @@ store( "myPlugin", { }, } ); ``` + The callback passed as the reference receives [the event](https://developer.mozilla.org/en-US/docs/Web/API/Event) (`event`), and the returned value by this callback is ignored. When the element is removed from the DOM, the event listener is also removed. @@ -414,14 +442,15 @@ The syntax of this directive is `data-wp-on-document--[document-event]` (like `d See store used with the directive above ```js -store( "myPlugin", { +store( 'myPlugin', { callbacks: { - logKeydown(event) { + logKeydown( event ) { console.log( 'Key pressed: ', event.key ); }, - }, + }, } ); ``` + The callback passed as the reference receives [the event](https://developer.mozilla.org/en-US/docs/Web/API/Event) (`event`), and the returned value by this callback is ignored. When the element is removed from the DOM, the event listener is also removed. @@ -439,13 +468,10 @@ You can attach several side effects to the same DOM element by using the syntax The `unique-id` doesn't need to be unique globally. It just needs to be different from the other unique IDs of the `wp-watch` directives of that DOM element. ```html -
    -

    Counter:

    - - +
    +

    Counter:

    + +
    ``` @@ -453,40 +479,41 @@ The `unique-id` doesn't need to be unique globally. It just needs to be differen See store used with the directive above ```js -store( "myPlugin", { - actions: { - increaseCounter: () => { - const context = getContext(); - context.counter++; - }, - decreaseCounter: () => { - const context = getContext(); - context.counter--; - }, - }, - callbacks: { - logCounter: () => { - const { counter } = getContext(); - console.log("Counter is " + counter + " at " + new Date() ); - }, - }, +store( 'myPlugin', { + actions: { + increaseCounter: () => { + const context = getContext(); + context.counter++; + }, + decreaseCounter: () => { + const context = getContext(); + context.counter--; + }, + }, + callbacks: { + logCounter: () => { + const { counter } = getContext(); + console.log( 'Counter is ' + counter + ' at ' + new Date() ); + }, + }, } ); ``` + The `wp-watch` directive is executed: -- When the element is created -- Each time that any of the properties of the `state` or `context` used inside the callback changes +- When the element is created +- Each time that any of the properties of the `state` or `context` used inside the callback changes The `wp-watch` directive can return a function. If it does, the returned function is used as cleanup logic, i.e., it will run just before the callback runs again, and it will run again when the element is removed from the DOM. As a reference, some use cases for this directive may be: -- Logging -- Changing the title of the page -- Setting the focus on an element with `.focus()`. -- Changing the state or context when certain conditions are met +- Logging +- Changing the title of the page +- Setting the focus on an element with `.focus()`. +- Changing the state or context when certain conditions are met ### `wp-init` @@ -498,7 +525,7 @@ The `unique-id` doesn't need to be unique globally. It just needs to be differen ```html
    -

    Hi!

    +

    Hi!

    ``` @@ -506,10 +533,10 @@ Here's another example with several `wp-init` directives on the same DOM element ```html
    - +
    ``` @@ -546,7 +573,7 @@ The `unique-id` doesn't need to be unique globally. It just needs to be differen ```html
    -

    Hi!

    +

    Hi!

    ``` @@ -554,38 +581,44 @@ The `unique-id` doesn't need to be unique globally. It just needs to be differen See store used with the directive above ```js -import { getElement, store, useState, useEffect } from '@wordpress/interactivity'; +import { + getElement, + store, + useState, + useEffect, +} from '@wordpress/interactivity'; // Unlike `data-wp-init` and `data-wp-watch`, you can use any hooks inside // `data-wp-run` callbacks. const useInView = () => { - const [ inView, setInView ] = useState( false ); - useEffect( () => { - const { ref } = getElement(); - const observer = new IntersectionObserver( ( [ entry ] ) => { - setInView( entry.isIntersecting ); - } ); - observer.observe( ref ); - return () => ref && observer.unobserve( ref ); - }, []); - return inView; + const [ inView, setInView ] = useState( false ); + useEffect( () => { + const { ref } = getElement(); + const observer = new IntersectionObserver( ( [ entry ] ) => { + setInView( entry.isIntersecting ); + } ); + observer.observe( ref ); + return () => ref && observer.unobserve( ref ); + }, [] ); + return inView; }; store( 'myPlugin', { - callbacks: { - logInView: () => { - const isInView = useInView(); - useEffect( () => { - if ( isInView ) { - console.log( 'Inside' ); - } else { - console.log( 'Outside' ); - } - }); - } - }, + callbacks: { + logInView: () => { + const isInView = useInView(); + useEffect( () => { + if ( isInView ) { + console.log( 'Inside' ); + } else { + console.log( 'Outside' ); + } + } ); + }, + }, } ); ``` + It's important to note that, similar to (P)React components, the `ref` from `getElement()` is `null` during the first render. To properly access the DOM element reference, you typically need to use an effect-like hook such as `useEffect`, `useInit`, or `useWatch`. This ensures that the `getElement()` runs after the component has been mounted and the `ref` is available. @@ -598,8 +631,8 @@ The key should be a string that uniquely identifies the element among its siblin ```html
      -
    • Item 1
    • -
    • Item 2
    • +
    • Item 1
    • +
    • Item 2
    ``` @@ -607,8 +640,8 @@ But it can also be used on other elements: ```html ``` @@ -624,9 +657,9 @@ For example, let's consider the following HTML. ```html
      - +
    ``` @@ -634,9 +667,9 @@ It would generate the following output: ```html
      -
    • hello
    • -
    • hola
    • -
    • olá
    • +
    • hello
    • +
    • hola
    • +
    • olá
    ``` @@ -644,9 +677,9 @@ The prop that holds the item in the context can be changed by passing a suffix t ```html
      - +
    ``` @@ -655,19 +688,21 @@ By default, it uses each element as the key for the rendered nodes, but you can For that, you must use `data-wp-each-key` in the `