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_levelis a string:<!-- wp:simpletoc/toc {"max_level":"3"} /--> -
Good —
max_levelis 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
ServerSideRendercomponent useswp.element.RawHTML()which wraps the output in adiv, hence you shouldn’t wrap theServerSideRenderin ap, 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 .../>