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');JavaScriptThe performance impact was significant. Our testing showed that:
- The old
registerBlockSingleProductTemplateexecuted 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:
- 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
- Architecture: Implemented Singleton pattern for consistent block management and better type safety
- Developer Experience: New API that mirrors WordPress core’s
registerBlockTypewith improved error handling - 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.
- registerBlockSingleProductTemplate: Refactor to use a BlockRegistrationManager class and Singleton pattern #53788
- Rename registerBlockSingleProductTemplate to registerProductBlockType #53872
- registerBlockSingleProductTemplate: Fix template switching #53874
- registerProductBlockType: Update signature and usages #53895
Leave a Reply