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

Is there any onAdd event for blocks? #8655

Closed
foysalremon opened this issue Aug 7, 2018 · 5 comments
Closed

Is there any onAdd event for blocks? #8655

foysalremon opened this issue Aug 7, 2018 · 5 comments
Labels
[Type] Help Request

Comments

@foysalremon
Copy link
Contributor

@foysalremon foysalremon commented Aug 7, 2018

Like on the title, I am willing to be able to add a callback while my custom block is added on editor. Like I am creating some countdown which is initializing by a jquery function call by taking data from html data attributes.

Now I am willing the function to be run while I have added a new countdown element on my editor (by taking it's default value).

@youknowriad
Copy link
Contributor

@youknowriad youknowriad commented Aug 7, 2018

There are no events in Gutenberg. The idea is that there are selectors that can give you at anytime the data in Gutenberg (for example the list of blocks in the post) and there's a unique event emitter you can use to track whether the state (entire state) change or not. For example you can do:

const getBlockList = () => wp.data.select( 'core/editor' ).getBlocks();
let blockList = getBlockList();
wp.data.subscribe(() => {
  const newBlockList = getBlockList();
  const blockListChanged = newBlockList !== blockList;
  blockList = newBlockList;
  if ( blockListChanged ) {
    // You can trigger here any behavior when the block list in the post changes.
  }
});

You can check the handbook for all the available selectors you can use this way https://wordpress.org/gutenberg/handbook/data/

it's also good to take a look at the documentation of the Data Module.

I hope this helps. Thanks

@foysalremon
Copy link
Contributor Author

@foysalremon foysalremon commented Aug 8, 2018

I have no idea how to use this on a targeted block. Here below is my code. I am willing to run the function countdown while this block is added on editor. This is currently running on onChange only

(function (wpBlocks, wpEditor, wpComponents, wpElement) {
    const {InspectorControls, RichText, MediaUpload, PlainText, ColorPalette} = wpEditor;
    const {registerBlockType} = wpBlocks;
    const {Button, PanelBody, PanelColor, FontSizePicker, DateTimePicker, SelectControl} = wpComponents;
    const {Fragment,Component} = wpElement;
    class CountDown extends Component {
        constructor() {
            super(...arguments);
            countdown();
        }
        render(){
            const { attributes, className, setAttributes, isSelected } = this.props;
            const {
                dateTime
            } = attributes;
            return (
                <Fragment>
                    <InspectorControls>
                        <PanelBody title="Set Date & Time" initialOpen={true}>
                            <DateTimePicker
                                currentDate={dateTime}
                                onChange={(currentDate) => {
                                    setAttributes({dateTime: currentDate})
                                    countdown()
                                }}
                            ></DateTimePicker>
                        </PanelBody>
                    </InspectorControls>
                    <div className="countdown">
                        <div
                            className="ex-countdown"
                            data-time={dateTime}
                        ></div>
                    </div>
                    {isSelected && (
                        countdown()
                    )}
                </Fragment>
            );
        }
    }
    registerBlockType('ex/countdown', {
        title: 'Countdown',
        icon: 'screenoptions',
        category: 'common',
        attributes: {
            dateTime: {
                type: 'string',
                default: '2019/08/18 00:00:00'
            }
        },
        edit: CountDown,
        save({attributes}) {
            const {dateTime} = attributes;
            return (
                <div className="countdown">
                    <div className="ex-countdown" data-time={dateTime}></div>
                </div>
            )
        }
    });
})(wp.blocks, wp.editor, wp.components, wp.element);

@youknowriad
Copy link
Contributor

@youknowriad youknowriad commented Aug 8, 2018

your block name is called ex/countdown so instead of comparing the entire list of blocks like I did above, you can filter the list of blocks by their name and count how many blocks with the name ex/countdown exist and if this count changes, it means one of your blocks have been added or removed.

@designsimply designsimply added the [Type] Help Request label Aug 15, 2018
@NewJenk
Copy link

@NewJenk NewJenk commented Nov 13, 2020

your block name is called ex/countdown so instead of comparing the entire list of blocks like I did above, you can filter the list of blocks by their name and count how many blocks with the name ex/countdown exist and if this count changes, it means one of your blocks have been added or removed.

@youknowriad how can you filter the list of blocks by their name? I'm currently using forEach but I'm not sure this is the most performant way:

const getBlockList = () => wp.data.select( 'core/block-editor' ).getBlocks();

getBlockList().forEach( function( blockType ){
	console.log( blockType.name );
});

@kalimah-apps
Copy link

@kalimah-apps kalimah-apps commented Oct 12, 2021

Using hooks

I think a better approach with the introduction of hooks in react 16.8 (the question was asked before that) is to use useEffect hook in block edit function.
Basically you can add this:

useEffect(() => {
	console.log('TESTING');
// notice the [] in the second parameter
}, []);

Adding empty array as a second parameter to useEffect will trigger the event on initial render.

To check for block removal you can return a function in useEffect hooks:

useEffect(() => {
	console.log('Inserted');
	return () => {
		console.log('Removed');
	};
}, []);

Comparing old vs new blocks

The code provided by @youknowriad is a good start but it was triggered many times especially when typing. This is because it monitors all changes. You can use this code to limit events and check if there has been an addition or removal of the block.

const { getBlocks: getBlockList } = wp.data.select('core/editor');

// Get current blocks client ids
let blockList = getBlockList().map((block) => block.clientId);

wp.data.subscribe(() => {
	// Get new blocks client ids
	const newBlockList = getBlockList().map((block) => block.clientId);

	// Compare lengths
	const blockListChanged = newBlockList.length !== blockList.length;

	if (!blockListChanged) {
		return;
	}

	// Block Added
	if (newBlockList > blockList) {
		// Get added blocks
		const added = newBlockList.filter((x) => !blockList.includes(x));
		console.log('added', added);
	} else if (newBlockList < blockList) {
		// Get removed blocks
		const removed = blockList.filter((x) => !newBlockList.includes(x));
		console.log('removed', removed);
	}

	// Update current block list with the new blocks for further comparison
	blockList = newBlockList;
});

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Type] Help Request
Projects
None yet
Development

No branches or pull requests

5 participants