Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clarify Interactivity API evaluate function and directives to not allow action usage for what should be state #69269

Open
felixarntz opened this issue Feb 20, 2025 · 1 comment
Labels
Developer Experience Ideas about improving block and theme developer experience [Feature] Interactivity API API to add frontend interactivity to blocks. [Type] Enhancement A suggestion for improvement.

Comments

@felixarntz
Copy link
Member

felixarntz commented Feb 20, 2025

This came up as part of working on #68097 in conversation with @luisherranz, specifically in the comments starting with #68097 (review).

Problems to address:

  • Some developers today rely on store functions such as actions to determine the value for attributes. However, values should always use context or derived state, so this is an anti-pattern that should be more strongly discouraged or even made impossible.
  • The evaluate() function today invokes functions, whereas in relation to the above it should only look up the function and return it, just like it does for regular values.

As part of the PR #68097, the behavior was already moved closer towards the right direction, but it's definitely in a temporary state. Here's a list of a few more low-level technical issues that should be ironed out for a proper solution:

  • Currently, every caller of evaluate() needs to check whether the return value is a function and, if so, invoke it. This should be made more ergonomic.
  • Currently, it is possible to provide reference to a function in combination with a negation operator. This should no longer be allowed because functions should only be used for actions like for data-wp-on, callbacks like for data-wp-watch etc - i.e. functions should never return a value, and thus a negation operator for them is irrelevant.

One part that's already prepared for a step in the right direction is that providing a reference to a function in combination with a negation operator will trigger a console warning going forward, since this should never be used, as functions should never be used to return values in the first place. For now it still works due to backward compatibility, but is now deprecated and can eventually be removed.

Let's discuss in this issue what would be a good strategy to get to the end goal. We also need to consider potential in-between steps, such as first deprecating something that is discouraged, launching that in a release, and only later making it impossible.

@felixarntz felixarntz added [Feature] Interactivity API API to add frontend interactivity to blocks. [Type] Enhancement A suggestion for improvement. Developer Experience Ideas about improving block and theme developer experience labels Feb 20, 2025
@felixarntz
Copy link
Member Author

felixarntz commented Feb 20, 2025

A potential final solution could be something like this:

  • evaluate() should under no circumstances invoke functions. It should simply return the function.
    • As such, the ...args should be removed from evaluate(), as no function would ever be invoked with them.
    • If the evaluated value is a function and a negation operator is provided, it should throw an error that this is invalid.
  • In order for directives not to manually have to check for whether the return value from evaluate() is a function or not, two wrapper functions for evaluate() should be added:
    • evaluateValue() would always return a value (e.g. scalar, object, or array), never a function.
    • evaluateFunction() would always return a function, never an actual value.
  • Each directive would use either evaluateValue() or evaluateFunction(), depending on the directive's purposes. All directives responsible for e.g. an attribute value would use evaluateValue() and all directives responsible for e.g. event listeners or callbacks would use evaluateFunction().

The wrapper functions could be as simple as something like this:

type NoFunctionValue = boolean | string | number | null | undefined | { [key: string]: NoFunctionValue } | Array<NoFunctionValue>;

const evaluateValue = ( entry: DirectiveEntry ): NoFunctionValue => {
	const result = evaluate( entry );
	if ( typeof result === 'function' ) {
		throw new Error( 'Value must not be a function.' );
	}
	return result;
};

const evaluateFunction = ( entry: DirectiveEntry ): Function => {
	const result = evaluate( entry );
	if ( typeof result !== 'function' ) {
		throw new Error( 'Value must be a function.' );
	}
	return result;
};

With those functions available, directives would never need to check what the value is, as it would be guaranteed to be the format each directive expects.

WDYT?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Developer Experience Ideas about improving block and theme developer experience [Feature] Interactivity API API to add frontend interactivity to blocks. [Type] Enhancement A suggestion for improvement.
Projects
None yet
Development

No branches or pull requests

1 participant