Gutenberg: Block SelectControl saves attributes but after a reload of Gutenberg it loads its defaults. Why?

The reason why the block editor uses the default value — for the max_level (“Maximum level”) attribute only — instead of the one parsed from the (block comment delimiter in the) saved post content, is because in the parsed/saved attributes, max_level is invalid.

Why is it invalid: Because the type of the attribute (value) doesn’t match the one defined when the block is registered — max_level: { type: 'integer', ... }.

And why doesn’t it match, is because this part which updates the attribute with a value that is a string:

// "level" here is an input value, and input values are string unless
// explicitly converted to another type (boolean, number, etc.):

onChange={ ( level ) => props.setAttributes( { max_level: level } ) }

Which then, for instance when the “Including H3” is selected as the “Maximum level”, results in "max_level":"3" instead of the correct one, "max_level":3:

  • Bad — max_level is a string: <!-- wp:simpletoc/toc {"max_level":"3"} /-->

  • Good — max_level is an integer: <!-- wp:simpletoc/toc {"max_level":3} /-->

So be sure to respect the attribute type and in your case, you would want to use Number( level ) like so:

props.setAttributes( { max_level: Number( level ) } )

BTW, I could’ve just pointed you to my answer here, but I thought it’d be better if I write an answer specific to this very question. 🙂

Additional Notes

  • The ServerSideRender component uses wp.element.RawHTML() which wraps the output in a div, hence you shouldn’t wrap the ServerSideRender in a p, which (in my case) resulted in an error (in the console) which says, “<div> cannot be a descendant of <p>” ( in fact, the regular HTML <p><div>something</div></p> is indeed invalid 🙂 ). So,

    // Instead of this:
    <p><ServerSideRender .../></p>
    
    // Use a div or Fragment as a wrapper:
    <div><ServerSideRender .../></div>
    <><ServerSideRender .../></>
    
    // Or simply, no wrapper:
    <ServerSideRender .../>