1

I'm building a custom block using something along the lines of:

const template = [
    ['core/paragraph', {
        placeholder: __('Choose a name.'),
        content: attributes.name ? attributes.name : ''
    }],
    ['core/paragraph', {
        placeholder: __('Another block.')
    }]
]

//...

<InnerBlocks
    allowedBlocks={['core/paragraph']}
    template={template}
    templateLock='all'
/>

At the moment it's working in the sense that attributes.name displays the correct content initially, but if the value of attributes.name changes, for example if I select a different name from a dropdown that supplies that value, then I have to update the post and refresh the page to see the new value.

Is there a way to have it change immediately? I tried using something like:

useEffect(() => {
    replaceInnerBlocks(
        clientId,
        createBlocksFromInnerBlocksTemplate(template)
    );
}, [attributes.name, clientId, template]);

This works without a page refresh, but it completely replaces the inner blocks and re-renders the entire template, so if the user changes the styling of the paragraph block, that gets lost and it all reverts to the default, just with the new content. Ideally I'd like to just update the content of that specific paragraph block when the attribute value changes, so that if, for example, the user changes the block's color settings, those settings are retained.

Yes I realize that if the user changes the content, their changes would be lost if the attribute changes -- that is fine for my use case.

Thanks for any help or leads!

1 Answer 1

0

Before using replaceInnerBlocks, merge/replace attributes of new inner blocks with existing inner blocks.

Code example;

const existingInnerBlocks = useSelect(
    (select) => {
        return select(blockEditorStore).getBlock(clientId).innerBlocks;
    },
    [clientId]
);

useEffect(() => {
    const newBlocks = createBlocksFromInnerBlocksTemplate(template);

    // condition when to replaceInnerBlocks(modify according to your requirements)
    if (
        newBlocks[0].attributes.content !== existingInnerBlocks[0].attributes.name
    ) {
        // store non Replaceable attributes
        const attributesNotReplaceable = newBlocks[0].attributes.content;

        // replace attributes
        newBlocks[0].attributes = existingInnerBlocks[0].attributes;

        // again replace attributes with non Replaceable attributes
        newBlocks[0].attributes.content = attributesNotReplaceable;

        //finally replaceInnerBlocks
        replaceInnerBlocks(clientId, newBlocks);
    }
}, [attributes.name, clientId, template]);

The important point here is using the existing inner block's properties/attributes to new inner blocks.

1
  • Thanks! I ended up figuring out a workaround a while back but your answer inspired me to make some adjustments. I ended up with something quite different but I did end up saving the attributes and applying them after updating the template, so that seems to work!
    – Shoelaced
    Mar 22, 2023 at 21:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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