-
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
Block Bindings: Allow async call in getValues
API
#66962
base: trunk
Are you sure you want to change the base?
Conversation
Size Change: +209 B (+0.01%) Total Size: 1.82 MB
ℹ️ View Unchanged
|
Interesting, what blockers did you encounter with using custom data stores that natively support async operations, through selectors and resolvers including the necessary caching layer? |
I am not sure I follow. Do you mean creating custom data stores for each bindings source that need async calls? |
You can create one data store in the plugin. It would be best to know what is currently possible to understand better how to improve the experience. Here are some docs:
|
* @return {Object} The values obtained from calling `getValues` asynchronously. | ||
*/ | ||
export function asyncBlockBindingsGetValues( state, source, args ) { | ||
return state.asyncBlockBindingsGetValues[ args.clientId ]?.[ source.name ]; |
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 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.
At some point, I was using Map to use args
as the key
and it was "working". Maybe we could explore that possibility if needed?
I know @chriszarate and @maxschmeling have done some work in this area and may have feedback. |
I've been testing this a bit further based on the suggestions and, without this pull request, this code to make async calls work as suggested by @gziolo 👍 const gravatarStore = createReduxStore("gravatar-store", {
reducer: (state = {}, action) => {
switch (action.type) {
case "RECEIVE_GRAVATAR_DATA":
return { ...state, [action.id]: action.data };
}
return state;
},
selectors: {
getGravatarData: (state, id) => {
return state[id];
},
},
resolvers: {
getGravatarData:
(id) =>
async ({ dispatch }) => {
const response = await fetch(
"https://api.gravatar.com/v3/profiles/" + id
);
const result = await response.json();
dispatch({
type: "RECEIVE_GRAVATAR_DATA",
id,
data: result,
});
},
},
});
register(gravatarStore);
registerBlockBindingsSource({
name: "santosguillamot/gravatar",
getValues: ({ bindings, select, context }) => {
const { getEditedEntityRecord } = select(coreStore);
const newValues = {};
for (const [attributeName, source] of Object.entries(bindings)) {
const { key, field } = source.args;
const { gravatar_id: id } =
getEditedEntityRecord("postType", context?.postType, context?.postId)
.meta || {};
const data = select(gravatarStore).getGravatarData(id);
newValues[attributeName] = data?.[key || field];
}
return newValues;
},
}); It is reactive and it looks cleaner. I'll keep exploring in this pull request if it can be abstracted so sources don't need to create their own store. |
In the example above, you can make the code even more concise, example: reducer( state = {}, { data, id, type } ) {
return 'RECEIVE_GRAVATAR_DATA' === type ? { ...state, [ id ]: data } : state;
}, Overall, it's 20-30 lines of code to register a store. If someone is familiar with the concept, then having another abstraction might make it even harder to reason about. However, I'm looking forward to what you come up with. |
Thank you for updating the documentation - https://developer.wordpress.org/block-editor/reference-guides/packages/packages-data/ |
What?
In this pull request, I'm exploring how to allow the possibility of making async calls in the
getValues
API. I can see many external sources wanting to call external APIs withfetch
/apiFetch
, and they need to beasync
and wait for the result to show it in the editor. Here is an example using the Gravatar API:Gravatar.API.mp4
In
trunk
, it was shows the fallback value because it doesn't wait for the response:Why?
I believe async calls should be supported and we need to explore the best way to do it.
How?
In this case, I'm creating a wrapper resolver that calls
source.getValues
asynchronously. Then, depending if the callback is async or not, we callgetValues
directly or with this new wrapper.I am not sure if this approach is correct, but it seems to work. It seems in other scenarios we are using
resolvers
for doing async calls.@gziolo I'd love to know your thoughts. I remember you mentioning the possibility of using thunks, so I'm happy to explore that if you consider it better.
Testing Instructions
I still need to think how tests could look like.