Gutenberg Block: Objects are not valid as a React child (found: [object HTMLDivElement])

I am trying to add some html from one of my functions the edit and save funktion. If I do it manually it works:

return <p>Hello World</p>;

But if I do it with a function that returns a dom element, I get this error:

Gutenberg Block: Objects are not valid as a React child (found: [object HTMLDivElement]).

But if I do a console.info chrome displays the html. Any idea why this does not work?

registerBlockType( 'simpletoc/toc', {
    title: __( 'SimpleTOC', 'simpletoc' ),
    icon: listul,
    category: 'layout',
    edit: function( props ) {
            const data = wp.data.select( 'core/block-editor' );
            const blocks = data.getBlocks();
            console.info(generateTOC(blocks));
      return generateTOC(blocks);
    },
    save: ( props ) => {
          return generateTOC(blocks);
    },
} );

function generateTOC(blocks){
    var div = document.createElement('div');
    var ul = document.createElement('ul');
    var h2 = document.createElement('h2');
    var headline = __( 'Table of Contents', 'simpletoc' );
  h2.appendChild(document.createTextNode(headline));

    blocks.forEach(function (item,index){
        var blockId = '';
        var slug = '';

        h2 = document.createTextNode(headline)
        var title="";
        if ( item['name'] === 'core/heading' ) {
            blockId = (item['clientId']);
            title = item.attributes.content;
            slug = item.attributes.content.toSlug();
            var li = document.createElement('li');
            li.appendChild(document.createTextNode(title));
            ul.appendChild(li);
        }
    });

    div.appendChild(h2);
    div.appendChild(ul);

    return div;
}

1 Answer
1

Instead of using raw DOM nodes, use the WP Element wrapper components and build it with wp scripts

For example here is an ESNext block using modern JS:

    edit() {
        return (
            <div style={ blockStyle }>
                Hello World, step 1 (from the editor).
            </div>
        );
    },

And again using raw JS without the build step:

( function( blocks, i18n, element ) {
    var el = element.createElement;
....
        edit: function() {
            return el(
                'p',
                { style: blockStyle },
                'Hello World, step 1 (from the editor).'
            );
        },
...
} )( window.wp.blocks, window.wp.i18n, window.wp.element );

Here’s what your edit function might look like using modern JS:

    edit: function( props ) {
        const data = wp.data.select( 'core/block-editor' );
        const blocks = data.getBlocks();
        setHeadingAnchors(blocks);
        const headings = blocks.map( ( item, index ) => {
            if ( item.name === 'core/heading' ) {
                return null;
            }
            return <li>{item.attributes.content}</li>;
        } );
        return <div>
            <h2>{ __( 'Table of Contents', 'simpletoc' ) }</h2>
            <ul>
                {headings}
            </ul>
        </div>;
    },

Then we can use wp scripts to build it:

https://github.com/WordPress/gutenberg-examples/blob/master/01-basic-esnext/package.json#L19-L22

As a result it will build the script and handle all the JSX and make sure it uses the WordPress element wrapper ( otherwise you’d have to use el( 'p' etc. It’ll even give you a PHP file that returns an array of JS dependencies for wp_enqueue_script so that your built JS stays super lean, and gets loaded at the correct time in the editor

Sidenotes:

  • item['name'] will give you issues, item is an object, not an array
  • indent! Indent correctly and consistently
  • It looks like you have a funciton setHeadingAnchors that meddles in the affairs of other blocks, I’d advise against this
  • You should use this code in your save function as well, the only thing your PHP needs to do is enqueue the JS, and ensure any assets needed on the frontend are loaded

Leave a Comment