Skip to content

iAPI router: Support lazy loaded derived state #70872

@luisherranz

Description

@luisherranz

Description

Sometimes the derived state needs to be defined with a PHP closure.

wp_interactivity_state( 'myProductPlugin', array(
  'list'    => array( 1, 2, 3 ),
  'factor'  => 3,
  'doubleFactor'  => 6,
  'product' => function() {
    $state   = wp_interactivity_state();
    $context = wp_interactivity_get_context();
    return $context['item'] * $state['factor'];
  }
));

In these cases, the serialized value in the product property of the myProductPlugin namespace is not a specific value, as it depends on the context. To know the value of that property, the JavaScript store has to be loaded, which includes the same logic in a getter.

const { state } = store( 'myProductPlugin', {
    state: {
        get doubleFactor() {
          return state.factor * 2;
        },
        get product() {
            const { item } = getContext();
            return item * state.factor;
        },
        // ...

The problem comes when the store is not loaded before hydration because it's somehow lazy loaded. In that case, state.product in the myProductPlugin namespace will return undefined, and the directives subscribed to that value will replace the value generated on the server with the PHP function with undefined.

  • Server-side rendering: <span data-wp-text="state.product">3</span> (3 generated by PHP function)
  • Client value after hydration: <span data-wp-text="state.product"></span> (3 removed because the value is now undefined)

The Interactivity API should be aware of which values were generated on the server via a PHP function and avoid replacing the directive's value until a getter is available that can correctly recalculate it.

Step-by-step reproduction instructions

  1. Add a block with a template and a data-wp-each directive that uses the list provided in the description's example: <template data-wp-each="state.list"><span data-wp-text="state.product"></span></template>
  2. In the view.js file, add the store with the getter using a setTimeout of a couple of seconds.

Screenshots, screen recording, code snippet

No response

Environment info

No response

Please confirm that you have searched existing issues in the repo.

  • Yes

Please confirm that you have tested with all plugins deactivated except Gutenberg.

  • Yes

Please confirm which theme type you used for testing.

  • Block
  • Classic
  • Hybrid (e.g. classic with theme.json)
  • Not sure

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions