-
Notifications
You must be signed in to change notification settings - Fork 4.3k
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
[PoC] Bindings API: handling data sync in the Model context #60173
Closed
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
retrofox
force-pushed
the
update/binding-data-store-handling
branch
from
March 25, 2024 14:45
5e4a1bd
to
d4791db
Compare
Size Change: +498 B (0%) Total Size: 1.72 MB
ℹ️ View Unchanged
|
retrofox
changed the title
Bindings API: handle sync-data-propagation at store level
Bindings API: handle sync-data-propagation at the app Model level
Mar 25, 2024
retrofox
changed the title
Bindings API: handle sync-data-propagation at the app Model level
Bindings API: handling data sync in the context of the App Model
Mar 25, 2024
retrofox
changed the title
Bindings API: handling data sync in the context of the App Model
Bindings API: handling data sync in the Model context
Mar 25, 2024
gziolo
added
[Type] Technical Prototype
Offers a technical exploration into an idea as an example of what's possible
[Feature] Block bindings
labels
Mar 28, 2024
retrofox
changed the title
Bindings API: handling data sync in the Model context
[PoC] Bindings API: handling data sync in the Model context
Mar 29, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Labels
[Feature] Block bindings
[Type] Technical Prototype
Offers a technical exploration into an idea as an example of what's possible
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
What?
Technical foundation
For data synchronization, we could say that we have considered two approaches. The first, the most developed so far, and the current one of the Block Editor, is to handle data propagation in the context of the application view through components.
The second one, which is no more than an idea afaik, handles data propagation in the application model, wholly decoupled from the view. This is the approach I want to share.
Handling data at the View level
Currently, the application creates a component instance (BindingConnection) for each connection between a block instance attribute and a property from an external source. Using a custom hook, it can “listen” for changes to this value and update it via the source handler API.
Handling data at the Model level
In this approach, data is propagated at the Application Model level.
Development and deployment require many more changes than the previous approach and, in some ways, require notable changes to the API.
Introduce
core/bindings
storeDue to the implementation’s nature and context, creating a store for the Bindings seems necessary. It is possible to register external source handlers and connections (we will discuss this later).
For example, the app dispatches an action from this store to register the core/post-meta source handler.
Binding block attributes to a source
As mentioned above, data propagation no longer occurs in the View but in the Application Model. Therefore, it should no longer happen in the context of a component (UI/block edit function/React) but should be where the application registers all data relevant to the editor block instances, that is, in the core/block-editor store.
It also means that custom React hooks no longer make sense in this context. In other words, no more useSource() for the source handlers.
Now, the application uses actions, selectors, and other mechanisms to sync the block instances’ attribute values with external sources.
We have extended the
core/block-editor
store with thebindings
property. In this sub-store, the necessary data is stored to record the connections between the blocks with attributes bound to some property from an external source.Why?
How?
Binding Custom fields workflow
A good way to understand how the implementation works is to go through step by step how the custom fields are linked to the attributes of a block using a classic example.
Let’s base ourselves on the following block composition:
In this composition, let’s focus on the paragraph, the image block, and the button block.
Each block has attributes that define its content. The paragraph is defined in the content attribute, the image block defines the image URL in the url attribute, and the alt text in the alt attribute. Finally, the button has the attributes text, and url.
Let’s outline the idea with the following graph:
Now, suppose we have two custom fields:
text_custom_field
andurl_custom_field
, and we want to bind them to these block instances. For this purpose, we first need the source handler, which already exists in the block editor:core/post-meta source
handler.To use this handler, we must register it in the bindings store.
Finally, the most exciting and intricate part is connecting the blocks’ attributes with a property from an external source.
Suppose we bind the "content" attribute to the text_custom_field property, creating a connection between them. Thus, the application registers this connection to react to any changes that both parts (the attribute and the property) may experience. Schematically:
Once this connection is established, when the attribute’s value changes, the external property’s value will be updated with this value, and vice-versa.
We could also connect other attributes to the same connection:
In the same way, it would be possible to create a new connection, but this time between the
url
attribute and theurl_custom_field
custom field:In the diagram above, I’ve added the store name and the fields where the data is stored. In the
core/block editor
, the app registers blocks with attributes bound to an external source through the connections.Without wanting to get too much into the technical details, the following screenshot shows how the data is organized:
Similarly, for the store
core/bindings
:How are changes in the external source detected?
It is possible to subscribe to a callback function every time a new connection is registered so that it is invoked when the value of the external source property changes.
This is the way the application observes changes. In some way, it would be the part that replaces the custom hooks.
Video
https://video.wordpress.com/v/YmJsgxUf
The video above shows how the data propagates in both directions. In the console, when dispatching the following action…
… the data propagates to the block attributes.
When dispatching an action to update the block attributes…
… the external sources get updated.
Also, it shows that when bound attributes are changed using the UI, the rest of them bound to the same connection get updated, too.
Finally, in the edition mode, it shows how the data propagates as a probe of the data handling at the Model level.
Testing Instructions
Testing Instructions for Keyboard
Screenshots or screencast