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>

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):

  • state has some client-side values (isLoading to handle form input’s disabled status and canBeReset to toggle the Reset button’s visibility)
  • actions:
    • setPerPage (for <select> control)
    • doSearch for….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

  • useFetch has 128 lines to handle data-fetching and preloading
  • useURL is 123 lines of code and has to use useCallback for proper memoization
  • Both rely on a utilities file 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

See something inaccurate?