-
Notifications
You must be signed in to change notification settings - Fork 98
Fix FM module loader in Gutenberg context #815
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
Conversation
|
@kjbenk So that folks can test more easily, could you post (or link to) example fields that are subject to the bug, and what the expected behavior is before and after the patch is applied? |
|
@dlh01 Thanks for the input :) I added some steps to reproduce the issue and the expected behavior. |
js/fieldmanager-loader.js
Outdated
| setTimeout(() => { | ||
| callback(); | ||
| }, 100); |
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 must be a way to do this without the setTimeout call, let's keep at it until we identify what that is. setTimeout is too fragile for 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.
Totally agree :) I figured starting here is better than nothing for the time being.
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 did some quick testing and something like this works. The only thing I do not like about this approach is that we are not checking any state within the subscribe function, but instead checking a DOM element. Ideally, the code we run within wp.data.subscribe would validate against the store, however, this might be a good option for now.
const wrappedCallback = () => {
if (document.querySelector('.block-editor-page')) {
const metaboxesInitializedUnsubscribe = wp.data.subscribe(() => {
// Metaboxes are rendered and in the DOM.
if ( document.querySelector('.edit-post-meta-boxes-area__container') ) {
callback();
// Clean up.
metaboxesInitializedUnsubscribe();
}
});
} else{
callback();
}
};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 think subscribing to the areMetaBoxesInitialized state is the right approach, but rather than using a setTimeout, I think we could wrap the callback in a polling function that checks for the existence of the initialized markup before firing our callback. The polling function would need to have an upper limit of the amount of times it would run, but should work great.
This uses a polling function, i.e. `setInterval` once metaboxes have been initialized in the block editor to check for the existence of the rendered metabox markup before firing callback functions that perform side effects on that markup.
|
I took a stab at updating @kjbenk's previous approach with a polling function in 08926e6. I think this a good enhancement on the original |
kevinfodness
left a 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.
Left a suggestion, but I like this a lot. 🍣
js/fieldmanager-loader.js
Outdated
| */ | ||
| const wrappedCallback = () => { | ||
| if (document.querySelector('.block-editor-page')) { | ||
| let pollAttempts = 0; |
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.
Rather than setting this to 0 here and hardcoding the number of attempts it can't be greater than below (5) why not set this to 5 (and maybe rename it to something like pollAttemptsRemaining) and decrement it instead? Then the check could just be whether there are any poll attempts remaining. Currently, the threshold for polling attempts is pretty far down into the logic.
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.
Good suggestion. Updated to decrement the counter in 1f4697f.
js/fieldmanager-loader.js
Outdated
| if (callbackExecuted || ! pollAttemptsRemaining ) { | ||
| clearInterval(pollForMetaBoxes); | ||
| } | ||
| }, 100); |
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.
Do we feel this is always going to be sufficient? It's only as little as a half a second -- but, if loading the meta boxes takes longer than that, I expect it would be blocking the event loop, so that's probably moot?
Using timeouts or intervals still feels like a flawed pattern to me. For what it's worth, setInterval doesn't appear once in the GB codebase. And as far as I could tell with a quick pass, setTimeout was only used in cases like animations, flashes, etc.
Further, is an interval even necessary? Once subscribed, won't this repeatedly get executed during ~any activity in the editor? And to that point, should this get properly unsubscribed once successfully executed? Does GB subscribe work like redux subscribe in that you get an unsubscriber and can unsubscribe? I actually don't know.
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, it looks like subscribing a listener returns a function to unsubscribe it, as with redux
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.
FWIW @joemcgill here is how I implemented the unsubscribe logic - c25c3ca#diff-92affb4a9da5be89b7b8cf47a3822c1eba297ee97c9f7487feaea5f214726c45R33
|
@mboynes If we want to be in explicit control of when the callback gets fired, I think we'll need some way of handling the timing within our listener. That said, you're correct in the pragmatic sense in that...
We can probably just rely on state changes here instead of an interval and properly unsubscribe the listener after the callback is called. I've updated to that approach in 6f00450 and refined in 621cfa3. This is much simpler and works well. |
Problem
Most FM initializes happens when the DOM is ready and only if that element is considered visible via the jQuery
.is(':visible')function. In a recent Gutenberg update, metabox contents are not rendered until after the DOM is ready. (related to - #793 (comment))This can lead to issues with initialization since some metabox elements will not be rendered properly on DOM ready and therefore will return
falsefor.is(':visible').Potentially closes - #812
Steps to Reproduce
Expected Behavior
Solution
When loading FM in a Gutenberg context, hook into the
areMetaBoxesInitialized(wait a short period of time) and perform all FM initializations. This ensures that all metabox elements are rendered and in the DOM before any.is(':visible')checks are executed.