Gutenberg: Block validation fails if editor is reloaded or post is edited. data.getBlocks(); is empty in save function

Put simply, your block misbehaves.

Given a set of parameters, the block should always give you the same result. The editor uses this as a check, and if the result does not match, then something is wrong and the blocks implementation has a bug.

In your blocks case, this is because when generating the markup, you fetch the data from scratch each and every time. As a result, if I pass it A, B, and C, I might get D, or I might get E, because your save function relies on external API calls and is no longer safely contained. You’re not using the props/attributes to generate the output, you’re using some other things. This is bad!

So instead, when you figure out what the TOC are supposed to be, store it in an attribute. You’ll need to declare the attribute exists, and use the setter it provides you.

This leads us to a new problem:

function buildTOC(props){

    const data = wp.data.select( 'core/block-editor' );
    const blocks = data.getBlocks();
    const headings = blocks.map( ( item, index ) => {
        if ( item.name === 'core/heading' ) {
            var slug = '';
            var hashslug = '';
            var blockId = ( item.clientId );

            slug = item.attributes.content;
            hashslug = '#' + slug;

            return <li><a href={hashslug}>{item.attributes.content}</a></li>;
        }
    } );

The function that figures out which headings are in the document, is the same function that builds the final components. The function does too much, doesn’t use dependency injection, and as a result, there is no separation of concerns. This needs to be refactored into 2 functions, and I would also suggest replacing your function, with a component e.g. something similar to this:

        edit: function ( props ) {
            const headings = find_headings( props );
            props.setAttributes( { headings: headings } );
            return <toc {...props} />;
        },
        save: function ( props ) {
            return <Toc  {...props} />;
        },

Where <Toc> is something like this:


function Toc( props ) {
    return ....
}