Repository navigation
Ensure Product Collection Block's queryId Uniqueness - #44522
Conversation
Since the `clientId` is already using a uuid we can just lean into it and avoid the extra dependency. The `clientId` is not persistent across page reloads but that's okay since we only need it on init.
Since we don't need it to be _perfectly_ stable, we can just set the `queryId` to the instance ID on every component mounting. This is fine and won't mark the attribute as dirty unless a collection block is added, deleted, or moved in just the right way.
When product collection blocks are copied the attributes are copied too. This includes the unique ID which then becomes not unique. This commit adds a check to ensure that the ID is always unique.
|
Hi @Aljullu, @imanish003, @kmanijak, Apart from reviewing the code changes, please make sure to review the testing instructions as well. You can follow this guide to find out what good testing instructions should look like: |
Test Results SummaryCommit SHA: e4bfe6c
To view the full API test report, click here. To view the full E2E test report, click here. To view all test reports, visit the WooCommerce Test Reports Dashboard. |
imanish003
left a comment
There was a problem hiding this comment.
I found one edge case where it doesn’t seem to be working as expected. I added “Featured” collection & made it a sync pattern. Here is a quick demo of what’s happening if I try to add two sync patterns on the same post. The post becomes unresponsive.
Screen.Recording.2024-02-12.at.12.22.46.pm.mov
In the Code Editor, the sync patterns are represented as follows:
<!-- wp:block {"ref":219} /-->This pattern includes a 'ref' attribute, which corresponds to a post ID. As far as I understand, sync patterns are stored as posts in the wp_posts table, as illustrated in the screenshot below. This situation seems to cause a race condition or an infinite loop. Can you please check if it also leads to race condition in Gutenberg PR?
|
Thanks @imanish003, I'll have to add patterns to the list of things I test blocks code with. I was also able to replicate this in the Gutenberg PR so I've returned that one to a draft to make sure it doesn't get merged and introduce problems. I'll have to dive into how these pattern blocks work, I'm not sure what causes the failure. |
|
@ObliviousHarmony I've had luck finding existing blocks with Then when the block is mounted, check other found instances of the block and determine if a new unique value needs to be set for the attribute. |
|
That's good to know @spencerfinnell, however, in this case the problem lies in the very nature of sync blocks. I documented my findings in the Gutenberg PR. Unfortunately, I think fixing it there will be much easier than fixing it here; even if fixing it there requires more discussion. Since we have a support policy with WordPress versions, if this requires an upstream change, we will have to wait a long time to take advantage of it. I think our best bet would be to lock the |
When a sync pattern's block is updated it causes other instances on the page to re-insert the block. This changes the ID and causes it to update it again, leading to a loop.
Co-authored-by: Karol Manijak <20098064+kmanijak@users.noreply.github.com>
id and queryId UniquenessqueryId Uniqueness
Rather than using an instanceId for blocks in patterns, we will use a randomized number with an offset. This stops collisions in both instances on the page and instances in sync patterns.
kmanijak
left a comment
There was a problem hiding this comment.
I tested the solution and it solves the problem of unique query IDs! I tested multiple scenarios and looks good!
You mentioned:
however, this won't cause any problems since the queryid only has to be stable across client renders.
But I see one problem. Every time merchant edits the page the query IDs may change or replace. The pagination is based on the query IDs and links with specific page number may get quickly outdated.
I may be overthinking it but that affects sharing links including page number. I can't assess if it's a blocker (probably not?) but still wanted to raise that for discussion. What do you think?
There's a better selector that we can use and a const variable for the offset would be easier to follow.
|
Thanks for the review @kmanijak!
This is mostly true! The
The crux of this argument would be why people share links to specific result pages. I would argue that, if someone was to do this at all, it would be with the intention that the recipient look at it shortly after receiving it. It's incredibly unlikely that It would change in that span of time, or for most folks, probably at all after the merchant has a store page they're happy with. Keep in mind that people can do whatever they want to the page as long as they don't change the collection blocks. Ultimately, I think we're always going to have some possible instability here. As we've discussed elsewhere, Gutenberg isn't really built with stable block identification in mind. While it's something that could probably be added without too much trouble, as it stands now, it's not really achievable without caveats like this one. I don't think this is a problem though in the current case. |
kmanijak
left a comment
There was a problem hiding this comment.
The crux of this argument would be why people share links to specific result pages. I would argue that, if someone was to do this at all, it would be with the intention that the recipient look at it shortly after receiving it. It's incredibly unlikely that It would change in that span of time, or for most folks, probably at all after the merchant has a store page they're happy with. Keep in mind that people can do whatever they want to the page as long as they don't change the collection blocks.
Yeah, agree with you!
Testing steps work as expected! And I'm preapproving.
However, I tried to use the logic related to synced patterns and usage of offsets but I must admit I didn't make it. I saw low query IDs (like instance IDs) within synced patterns and the query ID was duplicated on the frontend. Here's a video:
syncedpatterns.mov
The query param was query-6-page=5 which means query ID was 6.
Personally, I think it's ok as I accepted synced patterns work that way. If you agree as well we should remove the logic with the offset. Or if the behavior from video is not expected and IDs should be unique in synced patterns then logic requires revisiting 😅 Unless I'm doing something wrong!
imanish003
left a comment
There was a problem hiding this comment.
I've also tested the changes and share similar concerns (as @kmanijak) regarding the use of offset for Synced patterns. I'm uncertain about their benefits. Wouldn't we still end up with duplicate query IDs on the client side with the offsets? If that's the case, what's the advantage of using it? Perhaps there's something I've overlooked 🧐
Here is a quick demo:
Edited.mov
|
Thanks for taking a look you two!
Odd question @kmanijak: are you sure you ran a build? I've played around and I can't get the block in a pattern without it setting an offset ID. Did you check the
Due to the nature of sync patterns it is impossible to uniquely identify them. The best we can do is make sure that there are no collisions between blocks on the page and patterns, as well as different patterns that contain Product Collection blocks.
There are a few cases we need to consider:
We are calling This works great for the first two points since we can just re-index all of the blocks each time. These IDs only exists for the purpose of pagination and so it's okay if they aren't permanent as long as they're unique and non-volatile. The benefit of using the instance ID is that each time the block is initialized we can set the The problem with this is the third point. If we lock the My solution to this problem was to offset the |
@kmanijak When I was testing today again, I also faced this issue. I ran
Aha! It all makes sense now. Thank you for the detailed explanation 🙏🏻 So now there is only one edge case related to having same sync patterns in same page/post. As you said, this is nature of sync patterns so it's not possible to fix it. I believe we need to raise it in Gutenberg because this will be problem with Query Loop block too. With that said, I am going to Approve the PR, as it's working as expected. Really appreciate your patience and detailed explanations throughout this PR 🙌🏻 |
Already done @imanish003 😄 I opened a pull request alongside this one. Now that this PR is ready to merge I can pull the changes from it into the Gutenberg one and mark it as ready for review. |
|
I took another look at my Gutenberg PR and it looks like this sync block solution doesn't work with the latest version of Gutenberg @imanish003 @kmanijak. As far as I can tell, the support they're building for partial sync patterns (which will solve this correctly!) has broken the way that it changes the attributes. For now, I think it's fine to merge this PR, but we should revisit once the feature in Gutenberg is stable and we can take advantage of it. What do you two think? It will still work perfectly for normal blocks but will break if you have Gutenberg installed. I think that's kind of problematic but there's currently no support otherwise. |
kmanijak
left a comment
There was a problem hiding this comment.
Odd question @kmanijak: are you sure you ran a build? I've played around and I can't get the block in a pattern without it setting an offset ID. Did you check the data-query-id attribute on the client? Manish's video contains the expected ID re-assignment behavior.
So I was testing with WP 6.5 and Manish with WP 6.4. I double checked and I can see offset values in 6.4 but not in 6.5. I debugged and the offset logic is fired correctly but the updated queryID is not reflected on the frontend.
It proves your statement (although I assumed you meant the newer Gutenberg version aiming WP 6.6):
I took another look at my Gutenberg PR and it looks like this sync block solution doesn't work with the latest version of Gutenberg @imanish003 @kmanijak.
What do you two think?
Given the above, I think we should remove the offset logic already as it will only temporarily benefit those using WP 6.4 while it introduces some unnecessary complexity.
And the most important is that the remaining logic with the instance ID solves the initial problem which is ensuring unique ID when duplicating Product Collection.
Since this offset doesn't work on WordPress 6.5 we're just going to go ahead and remove it.
I agree, I've gone ahead and removed the offset logic since it serves no purpose. We can explore solving this more holistically in a year or so when WordPress 6.6 (hopefully) has support for partial patterns! |

Submission Review Guidelines:
Changes proposed in this Pull Request:
One of the caveats to block duplication is that all attributes are copied. This has caused some trouble because the
queryIdare expected to be unique. This PR makes some changes to the block initialization in able to ensure that both attributes have a unique value.queryId: This problem was also present in Gutenberg and I have applied the same fix to both this block and the Query Loop block. Since we only need the ID to remain stable across frontend page loads, we can just setinstanceIdeach time. It will only actually do anything if it changes so there's no problem with doing it this way.Closes #43311
How to test the changes in this Pull Request:
Using the WooCommerce Testing Instructions Guide, include your detailed testing instructions:
queryIdin the Code Editor.queryIdare different in the Code Editor.queryIdis different.queryIdshould re-index and still ensure uniqueness.Note: The
queryIdtracks the number of instances that have been created on the page. Switching between the code editor and visual editor will not reset the counter and so after switching back you'll see the IDs have a weird offset. This is fixed on the next editor reload but doesn't actually hurt anything.Changelog entry
Details
Significance
Type
Message
Comment