Refactoring Product Context Block Registration in WooCommerce

We often encounter solutions that grow organically over time, accumulating complexity as new features and edge cases are handled. Recently, I had the opportunity to refactor WooCommerce’s product specific block registration system, transforming a function-based approach into a more robust, pattern-oriented solution. Here’s what I learned along the way.

The Challenge

Our original system used registerBlockSingleProductTemplate, a function that handled block registration for product-related blocks. The existing implementation created new store subscriptions for every block registered, resulting in O(n) subscriptions where n is the number of blocks. Each subscription would independently:

  • Watch for template changes
  • Handle block registration/unregistration
  • Manage ancestor constraints
  • Track registration attempts

This meant that with 10 blocks, we had 10 separate subscriptions all watching for the same template changes and potentially trying to register/unregister blocks simultaneously. Not only was this inefficient, but it also led to race conditions and unreliable behavior.

// The old approach created a subscription for EACH block
subscribe(() => {
  // Each subscription independently:
  const editSiteStore = select('core/edit-site');  // Multiple store selects
  const templateId = parseTemplateId(editSiteStore?.getEditedPostId()); // Multiple template checks
  
  if (templateId changed) {
    unregisterBlockType(blockName);  // Race conditions possible here
    registerBlockType(blockName, settings); // And here
  }
}, 'core/edit-site');
JavaScript

The performance impact was significant. Our testing showed that:

  • The old registerBlockSingleProductTemplate executed 4,550 subscription callbacks during initial page load of the Single Product template in the Site Editor, with an accumulated execution time of 2.47s
  • Even worse, because the old code never unsubscribed, these callbacks continued to execute when interacting with the Site Editor canvas

It also had several other limitations:

  • Error handling and type safety could be improved
  • The API diverged from WordPress core conventions

The system worked most of the time, but could behave unpredictably.

The Solution

The refactor introduced a new BlockRegistrationManager class implementing the Singleton pattern, along with a more intuitive registerProductBlockType function. Key improvements include:

  1. Performance: Single store subscription for all blocks instead of one per block, significantly reducing overhead
    • Executes only 24 subscription callbacks (down from 4,550)
    • Reduces execution time to 0.63s (down from 2.47s)
    • Properly handles unsubscription to prevent callback accumulation
  2. Architecture: Implemented Singleton pattern for consistent block management and better type safety
  3. Developer Experience: New API that mirrors WordPress core’s registerBlockType with improved error handling
  4. Reliability: More robust handling of edge cases and context-specific constraints

You can check out the full refactor in these PRs if you want to dive into the nitty-gritty details. Oh, and here’s the file.

Leave a Reply

Your email address will not be published. Required fields are marked *