2

I've created a custom block that uses a WordPress core block as an innerBlock child; more specifically the core/embed block. What I'd like to do is listen for changes in the child block's attributes, so that I can respond to them in the parent block.

The custom block edit function returns the following:

return (
    <div { ...useBlockProps() } onChange={parentUpdate}>
        <InnerBlocks
            allowedBlocks={['core/embed']}
            template={EMBED_TEMPLATE}
            templateLock="all"
        ></InnerBlocks>
    </div>
);

And the parentUpdate function currently looks like:

const parentUpdate = () => {
    const thisBlock = select('core/editor').getBlocksByClientId(clientId)[0]
    const childBlock = thisBlock.innerBlocks[0]
    const videoURL = childBlock.attributes?.url
    console.log(videoURL)
}

The parentUpdate function fires whenever the url attribute in the child embed block changes but (perhaps unsurprisingly) it fires before the child event block updates, meaning I get the value prior to the change, not after.

How should I be approaching this, so that I can use the child attributes after the child block updates?

7
  • onChange isn't a block related event, it's a DOM node related event, I do not believe you can do this via props and attributes in the component as that's not how block data works, <InnerBlocks> is not the source of data for inner blocks, it's just the mechanism by which they're displayed, it gets that data from elsewhere. What are you trying to implement that requires this? What is your block supposed to do? There's no context provided here so it's difficult to advise on the appropriate actions to take
    – Tom J Nowell
    Jan 26 at 10:45
  • @TomJNowell Thanks for clarifying how innerBlock works (that was my assumption). What I'm trying to implement is a way to generally subscribe to attribute changes in child blocks, so that I may use those values in my own custom parent blocks—the specifics of what I need to do with those values are likely too broad to be of much use.
    – verism
    Jan 26 at 10:59
  • let us be the judge of that, you might think they're irrelevant but I disagree, share them and then it'll be clear for all to see, the probability that there is a better way to do what you want to do, or that the approach isn't quite right is very high. I would go as far to say that needing to do this at all implies something has gone wrong somewhere, e.g. a big yellow flag was that at no point was useSelect considered. I would also say that all the things you would apply this to actually have very different solutions and this is the optimal solution for none of them
    – Tom J Nowell
    Jan 26 at 13:36
  • @TomJNowell That's fair enough! Well in this particular instance, I'd like to use the url, supplied to the child embed block, to make an API call in my parent block to retrieve a video thumbnail. In Vue this can be done via refs, but I'm new to the React world and still getting to grips.
    – verism
    Jan 26 at 14:18
  • I don't believe that innerblocks locked to embeds is the solution here, IMO you would be better using the OEmbed APIs and inputs that the embed uses to choose a suitable video in a singular block with no children
    – Tom J Nowell
    Jan 26 at 15:29

1 Answer 1

2

You wouldn't, the need to do this is normally a code smell and a warning sign that you have done something wrong, or are trying to work against react and the way blocks are intended to work in some way.

However if you wanted to continue down the path in the wrong direction, you would rely on useSelect to acquire the information you wanted, directly accessing select isn't great and it also means you no longer get changes in your component.

But as I said, and as revealed in your comment, this is all a big hack to work around wanting to show embeds in a block, resulting in putting embed blocks in the inner blocks as children, forcing you to then implement this as a hack to get the information you need, an X Y problem. Don't do that.

Blocks should not depend on other blocks like this, they need to be composable like actual lego bricks. It's ok if two bricks were designed to work together, e.g. column and columns blocks, but it's not a good idea to repurpose a block for editor UI purposes like that.

Instead, read the edit component of the core/embed block at:

https://github.com/WordPress/gutenberg/blob/trunk/packages/block-library/src/embed/edit.js

It shows:

  • how to get a preview code and render it
  • handling loading spinners for when it's fetching the embed code
  • URL input for video links
  • sandboxes
  • embeddable vs unembeddable items

With these the URL would just become another prop on your block.

It may even be possible to instead adjust the embed block itself with additional attributes and components via filters to extend it if the goal of your task is to provide a modified UI for an embed. However the details of what your work is intended to implement are scarce.

3
  • Thanks for taking the time to help with this, I see now why my initial approach was a misguided one. In this case my intention was to add a thin wrapper around video (youtube, vimeo etc) embeds, and so composability never felt much of a factor. That being said, maybe extending it would be smarter. In any case, I appreciate your help with the original question.
    – verism
    Jan 26 at 16:22
  • I'm not sure if the filter method is the best for you without knowing more, but I've used this in the past to add additional sidebar controls/toolbar buttons, and to add wrapper elements with additional messages to the edit component, avoiding the need for a new block or a duplicate block
    – Tom J Nowell
    Jan 26 at 16:33
  • The one example I do think this approach is needed for though is table of contents blocks, but they would use the useSelect hook to fetch that data, that way when the store changes they would re-render the block. All the data the block needs to render needs to be stored in attributes though, or rendered in PHP
    – Tom J Nowell
    Jan 26 at 16:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.