-
Notifications
You must be signed in to change notification settings - Fork 4.5k
iAPI: Make state getters to be updated asynchronously with store()
#70974
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Conversation
Flaky tests detected in d6a46b5. 🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/16622733022
|
store()
Size Change: +143 B (+0.01%) Total Size: 1.91 MB
ℹ️ View Unchanged
|
if ( this.pendingGetter ) { | ||
this.consolidateGetter(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I just realized that doing the update at this point could make a peek()
call to return the wrong value if the call is made synchronously.
We should consider whether we want to support this case. 🤔
The following accounts have interacted with this PR and/or linked issues. I will continue to update these lists as activity occurs. You can also manually ask me to refresh this list by adding the If you're merging code through a pull request on GitHub, copy and paste the following into the bottom of the merge commit message.
To understand the WordPress project's expectations around crediting contributors, please review the Contributor Attribution page in the Core Handbook. |
What?
Closes #70874
Changes the way getters are updated when
store()
is called. Instead of synchronously, which can cause problems with stores that are loaded after hydration, they are now updated asynchronously in a microtask, ensuring the updates happen before Preact renders.In the case that a getter is accessed synchronously right after being updated, the pending update is consolidated at that moment to reflect the correct value.
Why?
See #70874.
The
store()
function of the interactivity API is intended to allow the following syntax:See how the returned
state
is used inside thestate.double
getter. When thestore()
function runs before hydration, it works just fine. But if it happens after hydration, and thestate.double
was already accessed, meaning that a computed was already created for that prop, the getter is evaluated beforestore()
returns, and the execution fails with the following error:This happens because of the Preact's
computed()
implementation, which updates all computeds synchronously when any of their sources change.How?
Instead of creating a custom
computed()
implementation, this PR adds asetGetterAsync
method toPropSignal
(the internal class that handles prop updates). That method makesPropSignal
getters to be updated in a microtask. As microtasks run right at the end of the current task, the update is ensured to happen before the next Preact render (scheduled for the next task).In addition, if the
getComputed()
function of thePropSignal
class is called and a pending getter update exists, the update is performed synchronously at that moment.Testing Instructions
Apart from the added unit tests and e2e tests, you can test the fix manually following these steps:
In a page or template with interactive blocks, add a Custom HTML block with this content:
Custom HTML
Save and visit the page with the Custom HTML block.
A button with the symbol
+
should be rendered, along with the words "Count" and "Double", each with its corresponding value.Press the button
+
a couple of times.Ensure the value of "Double" is consistent with the value of "Count".