Search code examples
javascriptwordpressjsxwordpress-gutenberg

Why is my Custom Gutenberg Block broken upon save?


This is my first time creating a custom Gutenberg block. I have been able to successfully generate a couple of blocks. However when I create a post and use the block it works but when I exit and go back into the post I get this error:

This block contains unexpected or invalid content

I did some research and I read that if the edit and save have inconsistent content it could cause this. I've been unable to find an inconsistency though and I could use some help. Below is the code that registers the block which does do as intended, but does not let me edit after creating the block the first time.

Help is appreciated!

const { registerBlockType } = wp.blocks;

const { 
    RichText, 
    InspectorControls, 
    ColorPalette,
    MediaUpload,
    RawHTML
} = wp.editor;

const { PanelBody, IconButton } = wp.components;

registerBlockType('myblog/call-to-action', {

    title: 'Call To Action',
    description: 'Block to generate a call to action',
    icon: 'align-full-width',
    category: 'widgets',

    //Custom Attributes
    attributes: {
        title: {
            type: 'string',
            source: 'html',
            selector: 'h2'
        },
        titleColor: {
            type: 'string',
            default: 'white'
        },
        backgroundColor: {
            type: 'string',
            default: 'blue'
        },
        body: {
            type: 'string',
            source: 'html',
            selector: 'p'
        }
    },
   //Built-in Functions
    edit({attributes, setAttributes}) {
        const{
            title,
            priceCard,
            titleColor,
            backgroundColor,
        } = attributes;

        //Custom Functions
        
        function onChangeTitle(newTitle) {
            setAttributes( { title: newTitle } );
        }

        function onChangePriceCard(newCard) {
            setAttributes( { priceCard: newCard } );
        }

        function onTitleColorChange(newColor){
            setAttributes( { titleColor: newColor } );
        }

        function onChangeBackgroundColor(newBackground) {
            setAttributes( { backgroundColor: newBackground })
        }

return ([
            <InspectorControls style={ { marginBottom: '40px' } }>
                <PanelBody title={ 'Headline Color' }>
                    <p><strong>Choose Title Color</strong></p>
                    <ColorPalette 
                        value={titleColor} 
                        onChange={onTitleColorChange} 
                    />
                </PanelBody>

                <PanelBody title={ 'Background Color' }>
                    <p><strong>Choose Background Color</strong></p>
                    <ColorPalette 
                        value={backgroundColor} 
                        onChange={onChangeBackgroundColor} 
                    />
                </PanelBody>
            </InspectorControls>,
            
            <div class="final-cta">
                <div style= { { backgroundColor:backgroundColor } } class="analysis">
                    <div class="custom-shape-divider-top-1606696223">
                        <svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 120" preserveAspectRatio="none">
                            <path d="M985.66,92.83C906.67,72,823.78,31,743.84,14.19c-82.26-17.34-168.06-16.33-250.45.39-57.84,11.73-114,31.07-172,41.86A600.21,600.21,0,0,1,0,27.35V120H1200V95.8C1132.19,118.92,1055.71,111.31,985.66,92.83Z" class="shape-fill"></path>
                        </svg>
                    </div>
                    <RichText 
                        key="editable"
                        tagName="h2"
                        placeholder="H2 Title" 
                        value= { title }
                        onChange= { onChangeTitle }
                        style= { { color: titleColor } }
                    />
                </div>
                <span class="price-card-cta-container">
                    <RichText 
                        key="editable"
                        tagName="p"
                        placeholder="Paste Shortcode" 
                        value= { priceCard }
                        onChange= { onChangePriceCard }
                    />
                </span>
            </div>
               
        ]);
    },

save({ attributes }) {
        const {
            title,
            priceCard,
            titleColor,
            backgroundColor,
        } = attributes;

        return (
            <div class="final-cta">
                <div style= { { backgroundColor:backgroundColor } } class="analysis">
                    <div class="custom-shape-divider-top-1606696223">
                        <svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 120" preserveAspectRatio="none">
                            <path d="M985.66,92.83C906.67,72,823.78,31,743.84,14.19c-82.26-17.34-168.06-16.33-250.45.39-57.84,11.73-114,31.07-172,41.86A600.21,600.21,0,0,1,0,27.35V120H1200V95.8C1132.19,118.92,1055.71,111.31,985.66,92.83Z" class="shape-fill"></path>
                        </svg>
                    </div>
                    <h2 style={ { color:titleColor } }>{title}</h2>
                </div>
                <span class="price-card-cta-container">
                    <RichText.Content tagName="p" value={priceCard} />
                </span>
            </div>
           
        );
    }
});

Solution

  • The attribute priceCard is missing from your attributes, eg:

    registerBlockType('myblog/call-to-action', {
        ...
        //Custom Attributes
        attributes: {
            ...
            priceCard: { // is missing
                ... // set type, default etc..
            }
        },
        ...
    }
    

    The attribute was either added/removed in the updated block then when the saved post is reloaded, the attribute is missing causing the issue.

    Also, when doing simple attribute updates, the custom functions section in your code can be removed in favor of using setAttributes() directly, eg:

    <RichText
        key="editable"
        tagName="p"
        placeholder="Paste Shortcode"
        value={priceCard}
        onChange={(value) => setAttributes({ priceCard: value })} // attribute: value
    />
    

    This will make your code easier to manage/troubleshoot.