Skip to content
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

Execute filter once on block registration #5006

Closed
JasonHoffmann opened this issue Feb 12, 2018 · 3 comments
Closed

Execute filter once on block registration #5006

JasonHoffmann opened this issue Feb 12, 2018 · 3 comments
Labels
[Feature] Extensibility The ability to extend blocks or the editing experience [Type] Question Questions about the design or development of the editor.

Comments

@JasonHoffmann
Copy link
Contributor

Issue Overview

When creating a custom block, I have a variable that I want to make available for filtering in case other developers want to customize the functionality further. The data I am using is an array that is used to populate a dropdown menu. I know for that to work I can add something like:

let myArr = [ 'option 1', 'option 2' ];
myArr = wp.hooks.applyFilters( 'blocks.customBlock.filterArray', myArr );

And that would allow another developer to add something like:

function addToCustomArray( arr ) {
	return [ ...arr, 'option 3' ];
}
wp.hooks.addFilter( blocks.customBlock.filterArray', 'custom/namespace', addToCustomArray );

Which all works together fine in theory. However, I do not know where to put the applyFilters code in my call. If I add it to my edit function, then it is executed at least twice on page load, and more if there are multiple blocks of the same kind. The same goes for the save function. So my array ends up looking something like:

[ 'option 1', 'option 2', 'option 3', 'option 3', 'option 3', 'option 3' ]

If I try to add the above code at the very top of a code block, before my registerBlockType function is even called, then it doesn't load at all since (I think) wp.hooks hasn't loaded yet.

Is there a way for me to execute the above to apply a filter to a variable just once preferably when a block is executed or registered initially? I don't think there's something like init in the registerBlockType object, but I wanted to see if I was missing something.

Happy to provide more details if necessary.

@gziolo gziolo added the [Feature] Extensibility The ability to extend blocks or the editing experience label Feb 13, 2018
@Shelob9
Copy link
Contributor

Shelob9 commented Feb 14, 2018

@JasonHoffmann I'd love to get your thoughts on #5012 ? We're talking about communication between blocks, and making sure the current APIs include all of these kinds of needs. I think if you need to share a variable between blocks, a better pattern may be wp.data().

I think the quick fix, if you're going to use wp.hooks for your problem is to check wp.hooks.didFilter( 'blocks.customBlock.filterArray' ) like you'd use did_action() if this were PHP code.

I wonder, if registering a store on wp.data that your blocks or other blocks can access and you can inject into components, is a better pattern for this use, or if you need something else:

//Create reducer
const reducer = (state = {}, action) => {
    //Raise or lower/count and return
    switch (action.type) {
        case 'CHANGE_THING':
            return state.things = action.value;
        default:
            return state;
    }
};

//Register reducer with WordPress
const state = wp.data.registerReducer( 'yourPlugin', reducer );

//Register selector to get the count from this store
wp.data.registerSelectors('yourPlugin', {
    getThing: (state) => {
        return state.value;
    }
});

I appreciate your thoughts on if this would work, or what else plugin devs like us need.

@gziolo
Copy link
Member

gziolo commented Feb 15, 2018

@JasonHoffmann, it would be much easier to answer your question when I could see full code, but let me try to debug what could happen.

let myArr = [ 'option 1', 'option 2' ];
myArr = wp.hooks.applyFilters( 'blocks.customBlock.filterArray', myArr );

I assume you have those code inside edit. Given that you initialize myArr before filters get applied it should always work as expected - apply all registered filters. This isn't the most efficient approach because every time edit renders it is computed from scratch but technically it is okey. One way to mitigate that would be to extract this functionality into a helper function and use once method from Lodash like this:

// outside of edit
import { once } from 'lodash';
const getMyArray = () => {
    return wp.hooks.applyFilters( 'blocks.customBlock.filterArray', [ 'option 1', 'option 2' ] );
};

// inside edit
const myArray = getMyArray();

It will be executed for the first time when your block renders, so all other plugins should have enough time to register their own modifications. once is executed only once and then uses cached return value so you wouldn't be able to modify this afterward without some additional mechanisms.

function addToCustomArray( arr ) {
	return [ ...arr, 'option 3' ];
}
wp.hooks.addFilter( blocks.customBlock.filterArray', 'custom/namespace', addToCustomArray );

This code is valid and should append option 3 only once. Are you sure that it is called outside of registerBlockType? That would explain why this filter could be applied more than once. Otherwise, it should work properly.

If I try to add the above code at the very top of a code block, before my registerBlockType function is even called, then it doesn't load at all since (I think) wp.hooks hasn't loaded yet.

Yes, that is expected when your plugin loads first before other plugins have a chance to register their filters. This is because filters are applied and stored in the variable at the time when your file is loaded and parsed by the browser. This is expected behavior when using JS modules.

Is there a way for me to execute the above to apply a filter to a variable just once preferably when a block is executed or registered initially? I don't think there's something like init in the registerBlockType object, but I wanted to see if I was missing something.

I don't think we need it at the moment. All blocks are registered when editor instance gets loaded, so there is plenty of time for all plugins to register their own hooks. We can revisit that later if we discover that it blocks something for plugin developers, but so far it wasn't the case. I hope that you will be able to make your example work with the explanations I shared above. Let us know how it goes.

@gziolo gziolo added the [Type] Question Questions about the design or development of the editor. label Feb 15, 2018
@gziolo
Copy link
Member

gziolo commented Feb 15, 2018

I wonder, if registering a store on wp.data that your blocks or other blocks can access and you can inject into components, is a better pattern for this use

If you want to share data between a couple of components, then definitely wp.data would be a better approach. I second what @Shelob9 said. If you had any feedback to share #5012 is the best place to do it.

I will close this one as there are no actionable items for us. Feel free to reopen if you have more questions or I missed something. Always happy to help 👍

@gziolo gziolo closed this as completed Feb 15, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Feature] Extensibility The ability to extend blocks or the editing experience [Type] Question Questions about the design or development of the editor.
Projects
None yet
Development

No branches or pull requests

3 participants