Using The Interactivity API instead of React (part 3): The Javascript
In part 1 we talked about why, in part 2 we talked about the markup, and now we’re finally getting into the interactivity bit (aka the Javascript-but-technically-it’s-Typescript).
Init Interactivity
First off, let’s look at the container; we initialize the server state and set up a callback.
<?php
/**
* The archive-veteran.php file
*/
$i11y = new Interactivity_Helper();
get_header();
ob_start();
wp_interactivity_state( $i11y::STORE, $i11y->get_server_state() );
?>
<div data-wp-interactive="<?php echo $i11y::STORE; ?>" data-wp-router-region="<?php echo $i11y::ROUTER_REGION; ?>" data-wp-watch="callbacks.syncState">
<!-- The archive-veteran.php markup you've seen before -->
</div>This callback is the most important thing, because state is preserved across client-side renders with the interactivity router and we generally don’t want that!
The Server State
We build server-side state by parsing the URL params (hence $this->parse_url()) so that the URL is the source of truth. The page then renders the cards because the query loop has been modified.
/**
* Gets server state
*/
public function get_server_state(): array {
$this->parse_url();
global $wp_query;
$this->total_pages = $wp_query->max_num_pages;
return array(
'searchTerm' => $this->search_term,
'currentPage' => $this->current_page,
'perPage' => $this->per_page,
'totalPages' => $this->total_pages,
'militaryBranch' => $this->military_branch,
'decoration' => $this->decoration,
'war' => $this->war,
);
}
/**
* Modify the main query
*
* @param WP_Query $query The main query object.
*/
public function modify_query( WP_Query $query ) {
if ( is_admin() || ! $query->is_post_type_archive( 'veteran' ) || ! $query->is_main_query() ) {
return;
}
$this->parse_url();
$query->set( 'posts_per_page', $this->per_page );
$query->set( 'orderby', 'title' );
$query->set( 'order', 'ASC' );
$query->set( 'paged', $this->current_page );
if ( '' !== $this->search_term ) {
$query->set( 's', $this->search_term );
$query->set( 'relevanssi', true );
}
$tax_query = $this->set_taxonomy_filters();
if ( ! empty( $tax_query ) ) {
$query->set( 'tax_query', $tax_query );
}
}The Server State Sync Callback
All we do here is sync up the client-side state with the server-side state every time state changes (via page navigation or other interaction) because of wp-watch (which watches the state).
callbacks: {
syncState() {
const serverState = getServerState< ServerState >();
Object.assign( state, serverState );
const { searchTerm, militaryBranch, decoration, war } = state;
state.canBeReset = [
searchTerm,
militaryBranch,
decoration,
war,
].some( Boolean );
},
}The Store
From here, the interactivity store’s API is relatively small (~120 lines):
statehas some client-side values (isLoadingto handle form input’sdisabledstatus andcanBeResetto toggle the Reset button’s visibility)actions:setPerPage(for<select>control)doSearchfor….searching.navigateToPage(used by pagination buttons)navigateTo(the reusable interactivity router method)prefetch(the interactivity router prefetch)filterBy(for filtering by taxonomy)
callbacks:syncState(which we’ve already talked about)
And that’s basically it! Let’s compare that to a snippet of the React code: the useFetch and useURL hooks
useFetchhas 128 lines to handle data-fetching and preloadinguseURLis 123 lines of code and has to useuseCallbackfor proper memoization- Both rely on a
utilitiesfile which is itself 112 lines of code.
Just to understand the basic logic of getting veterans and storing state in the URL, you have to read through ~350 lines of semi-optimized React code that’s abstracted away from the presentation layer, compared to some bare bones PHP and a few lines of Typescript:
- 9 lines of
parse_url - 13 lines of
get_server_state - 3 lines of
callbacks.syncServerState