-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
Refactor the core/data store to be independent from the registry #14634
Conversation
00b6097
to
9044dc5
Compare
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 like the direction of this.
Build a "virtual" store added to all registries but proxify its selectors/actions per store
I'm assuming this refers to the core/data
generic store instantiated for each created registry. If so, I have to admit the terminology gets me tripped up sometimes because a registry can have multiple stores registered on it, so the above sentence reads better to me as "proxify its selectors/actions per registry".
Bake the controls plugin in the namespace stores (it wasn't possible to keep it separate and bake "core/data")
I think this seems sensible given the functionality of controls.
With the controls plugin being baked in, I wonder if - in a separate pull after this lands - if we should also bake in some common controls often used. Things like:
|
@nerrad or maybe a dedicated |
Oh ya, even better. That's something that can be done irrespective of this landing. I might give a go at it sometime soon. |
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 personally don't have an issue with making controls
plugin default behavior. The documentation should be updated accordingly.
} | ||
let resolvers; | ||
const actions = mapActions( { | ||
...metadataActions, |
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.
An initial thought occurred to me that there's risk for conflict between these metadata actions and a store's own. On further consideration, I don't see it likely to occur, or at least it should be known that these "baseline" actions/selectors exist for every store. Which leads me to wonder: How do we document this?
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.
yes, I assume these can't be auto-documented. I'll see if I can add a note in the data module README (where we document registerStore
)
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.
yes, I assume these can't be auto-documented.
I'm not so overly concerned about the selectors themselves (though it would be nice), more to just general awareness that this is a behavior to expect, the fact that resolution behavior is included in every store by default.
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.
There's potential side-effects here of consuming code being able to "override" default metadata selectors and actions. I almost wonder if there should be some enforcement here: i.e. checking the incoming actions/selectors and ensuring there are no collisions with the corresponding metadata functions? (or maybe change the order in the constructed array so metadata always overwrites the stores own?).
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.
If we're not confident exposing these selectors and actions yet, we can easily add __internal
or __experimental
for now, what do you think?
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'm less concerned with exposing them (I think they should be exposed?) and more concerned with potential conflicts.
I do think it'd be better (at least initially) to do this:
// for actions
const actions = mapActions( {
...options.actions,
...metadataActions,
} );
// and something similar with selectors reversing the order
That way the metadataActions and metadataSelectors take precedence. Of course you may intentionally have them this way so they can be overridden by the incoming store config but if not, I think this is safer.
let selectors = mapSelectors( { | ||
...mapValues( metadataSelectors, ( selector ) => ( state, ...args ) => selector( state.metadata, ...args ) ), | ||
...mapValues( options.selectors, ( selector ) => { | ||
if ( selector.isRegistrySelector ) { |
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.
This logic occurs for every selector call? In addition to the overhead of mapSelectors
? Should expect a negative performance impact?
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 wonder if it's not unreasonable to consider/implement what we have currently as metadata
and root
as in-fact two separate stores, so that we don't have to override the selector call to reach into state to provide the corrected "root" state.
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.
to your second point, in theory, a store could be just a scalar value which would break this proposal.
to your first point, this only happens when registering the store which I guess is fine. (only run once)
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.
to your first point, this only happens when registering the store which I guess is fine. (only run once)
Subsequent calls would still be subject to going through the ( state, ...args ) => selector( state.root, ...args )
proxying? May be small overhead; curious though given the reality of many thousands of selector calls during typical use.
Anything that you consider a blocker with this. I'd like to land this to unblock a few other PRs |
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.
Not a blocker, but I do think (as slim as it is) there's potential for conflict with the resolution selectors (less so actions) (see my earlier comment).
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 wasn't able to easily test this against my aforementioned use-case since it requires a combination of this and #14369, but conceptually this seems like a good solution to the general problem.
Still curious about what I'm seeing with Redux DevTools, but preemptively approving.
Isn't that because resolution state is stored with the selector as the key (i.e no longer need to store indexed by store name) |
Ah! I think you're right. I still see it as unnatural to have those as direct descendents of a "metadata" key. The reducer is called Small naming quibble, I'm not overly concerned about it now that it's been explained. |
@aduth It does seem like we could add a |
await fulfillment.fulfill( selectorName, ...args ); | ||
fulfillment.finish( selectorName, args ); | ||
store.dispatch( metadataActions.startResolution( selectorName, args ) ); | ||
await fulfillResolver( store, mappedResolvers, selectorName, ...args ); |
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 seem to be running into an issue where this doesn't actually wait for the fulfillment to complete before marking the resolution as finished:
(Note in last three actions, I'd have expected "RECEIVE_ITEMS" to have occurred prior to "FINISH_RESOLUTION")
I tried to consider how to reproduce this outside my branch, but since we wait for most all data to be available before rendering anything, it's hard to recreate even when adding a filter like add_filter( 'block_editor_preload_paths', '__return_empty_array' );
. Which has me wondering if it could in-fact be an issue which had already existed prior to this change, and only became evident when performing resolution for data which hadn't been initially requested in the display of the editor (in my case, waiting to resolve getPostType( 'wp_block' )
when saving a reusable block). In my branch, since the resolution completes before the data is available, it results in an error because postType
in this assignment resolves to undefined
.
I'll try to clean up my local working branch to push later this afternoon, but raising now in case anything comes to mind.
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 found the issue. I'll have a pull request shortly.
Edit: #14711
Related #14369
There's a lot in this PR but I don't know if it's breakable into smaller items. The main ideas here:
This is not ready as I didn't check the tests yet.