The WordPress Interactivity API, introduced in WordPress 6.5, marked a significant evolution in how developers build dynamic experiences within the Block Editor ecosystem. Moving beyond simple toggles and counters, the true power of this API is unlocked when tackling complex, interconnected state logic. This is where getters—the Interactivity API’s mechanism for Derived State—become indispensable. They allow developers to maintain a clean, normalized state while effortlessly calculating complex values and conditions on the fly.
This article explores the advanced use of Interactivity API getters, demonstrating how they enable sophisticated state management, improve code maintainability, and form the foundation for building complex, reactive WordPress blocks. For foundational understanding of state types, see [Mastering Global, Local, and Derived State in the WP Interactivity API](Mastering Global, Local, and Derived State in the WP Interactivity API.md).
The Foundation: State, Context, and Derivation
To appreciate the role of getters, one must understand the core state management model:
| Concept | Description | Mutability | Primary Use Case |
|---|---|---|---|
| Global State | Data accessible across the entire page, single source of truth for application-wide data. | Writable | Cross-block communication, application-wide settings. |
| Local Context | Data defined within a specific element’s HTML, accessible only to that element and its children. | Read-only (from JavaScript) | Per-instance state for individual blocks. |
| Derived State | Values computed from Global State or Local Context, calculated on-demand and cached. | Read-only | Complex calculations, conditional flags, data transformation. |
The principle is simple: store the minimum necessary data in mutable state, and derive everything else. This is the essence of a single source of truth.
The Indispensable Role of Getters
In the Interactivity API, Derived State is implemented using JavaScript object property getters within the state object:
// In view.js - Define store with Derived State
import { store } from '@wordpress/interactivity';
const { state } = store( 'myPlugin', {
state: {
// Core state: raw data
items: [
{ id: 1, name: 'Laptop', price: 1200, inStock: true },
{ id: 2, name: 'Mouse', price: 25, inStock: true },
{ id: 3, name: 'Keyboard', price: 75, inStock: false },
],
// Derived State using getters
get totalInStockValue() {
return state.items
.filter( item => item.inStock )
.reduce( ( sum, item ) => sum + item.price, 0 );
},
get outOfStockCount() {
return state.items.filter( item => !item.inStock ).length;
},
},
} );
Code language: JavaScript (javascript)When a directive references state.totalInStockValue, the getter function executes. The Interactivity API’s reactivity system tracks dependencies—if state.items changes, the getter automatically re-executes and bound components update.
Benefits in Complex Logic
- Purity and Consistency: Getters are pure functions of state, guaranteeing derived values always match the underlying data.
- Simplified Actions: Actions only modify raw state; complex calculations are encapsulated in getters.
- Performance Optimization: Getters only recalculate when dependencies change, preventing unnecessary computations.
- Readability: Complex expressions are encapsulated in descriptive getter names.
Implementing Advanced Derived State Patterns
Pattern 1: Filtering and Sorting Complex Data Structures
A common scenario is managing lists that can be filtered, sorted, or paginated. Getters are perfect for this—the raw list remains untouched, and the getter provides the “view” of the data.
Consider a block displaying posts with filtering and sorting options:
// In view.js - Complex filtering and sorting with getters
import { store, getContext } from '@wordpress/interactivity';
store( 'postList', {
state: {
// Raw data
posts: [ /* array of post objects */ ],
// Complex Derived State
get filteredAndSortedPosts() {
const { selectedCategory, sortBy } = getContext();
let result = state.posts;
// Filtering logic
if ( selectedCategory !== 'all' ) {
result = result.filter( post => post.category === selectedCategory );
}
// Sorting logic
if ( sortBy === 'date' ) {
result = result.sort( ( a, b ) => new Date( b.date ) - new Date( a.date ) );
} else if ( sortBy === 'title' ) {
result = result.sort( ( a, b ) => a.title.localeCompare( b.title ) );
}
return result;
},
},
} );
Code language: JavaScript (javascript)The getter uses getContext() to access local filtering parameters. Any change to Local Context or Global State automatically triggers re-execution of filteredAndSortedPosts.
Pattern 2: Conditional Flags for UI State
Getters excel at creating boolean flags representing complex UI conditions, keeping HTML directives clean:
| Complex Condition | Encapsulated Getter | HTML Directive |
|---|---|---|
| Cart has items AND user logged in AND address valid | get isCheckoutEnabled() | data-wp-bind--disabled="!state.isCheckoutEnabled" |
| Current step is 3 AND form valid AND not submitting | get canProceedToNextStep() | data-wp-bind--hidden="!state.canProceedToNextStep" |
| Filter text entered AND no results found | get showNoResultsMessage() | data-wp-bind--show="state.showNoResultsMessage" |
By abstracting logic into getters, HTML remains declarative and focused on what to display rather than how to determine it.
Pattern 3: Multi-Level Derived State (Getters Calling Getters)
For highly complex applications, chain getters to create layers of Derived State:
// In view.js - Multi-level Derived State
store( 'dashboard', {
state: {
// Raw State
rawSalesData: [ /* ... */ ],
// Level 1 Getter: Aggregate raw data
get aggregatedSales() {
return this.rawSalesData.reduce( /* grouping logic */ );
},
// Level 2 Getter: Calculate metric from aggregated data
get averageMonthlySales() {
const totalMonths = Object.keys( this.aggregatedSales ).length;
const totalSales = Object.values( this.aggregatedSales )
.reduce( ( sum, val ) => sum + val, 0 );
return totalSales / totalMonths;
},
// Level 3 Getter: Determine UI flag from metric
get showPerformanceAlert() {
return this.averageMonthlySales < 5000;
},
},
} );
Code language: JavaScript (javascript)This cascading reactivity ensures changes in rawSalesData automatically propagate through all derived layers without manual coordination.
Server-Side Derived State with PHP
The Interactivity API can process state on the server during initial render, crucial for performance and SEO. For simple Derived State, calculate values directly in PHP:
<?php
// In render.php - Simple server-side Derived State
$counter = 5;
$double = $counter * 2; // Derived state calculation
wp_interactivity_state( 'myCounterPlugin', array(
'counter' => $counter,
'double' => $double,
) );
?>
Code language: HTML, XML (xml)For complex Derived State depending on Local Context within loops, use PHP closures:
<?php
// In render.php - Dynamic Derived State with closures
wp_interactivity_state( 'myProductPlugin', array(
'factor' => 3, // Global State
// Derived State as dynamic function
'productTotal' => function() {
$state = wp_interactivity_state();
$context = wp_interactivity_get_context();
// Calculate: current item price * factor
return $context['itemPrice'] * $state['factor'];
},
) );
?>
<!-- HTML with Local Context in loop -->
<div data-wp-interactive="myProductPlugin" data-wp-each--product="state.products">
<div <?php echo wp_interactivity_data_wp_context( array( 'itemPrice' => 'product.price' ) ); ?>>
Total: <span data-wp-text="state.productTotal"></span>
</div>
</div>
Code language: PHP (php)The server-side renderer dynamically computes the total for each product based on its Local Context and Global State, ensuring correct initial HTML.
Getters and the Principle of Immutability
Getters are strictly read-only. They compute and return values, never modify state.
| Action | Result | Corrective Action |
|---|---|---|
state.myGetter = newValue; | Error: Cannot assign to getter | Update the raw state values the getter depends on |
state.myGetter.push( newItem ); | Error: Mutating getter return value | Copy the array/object, modify the copy, replace raw state |
This enforcement of immutability prevents side effects and maintains state model integrity. Treat getter return values as immutable to prevent accidental state corruption.
Conclusion
The WordPress Interactivity API’s implementation of Derived State through getters is the key to managing complexity. By moving conditional and transformational logic out of imperative actions and into declarative getters, developers achieve superior maintainability, enhanced performance through automatic dependency tracking, and unwavering consistency via the single source of truth principle. Mastering getters is essential for building professional-grade, highly interactive WordPress blocks.
