I solved the problem by rendering my block’s content, a component, to a string and making that string the value of a RichText component in my block’s save function.
To make my component a string, I used renderToString:
// Create the content and the block
const myContent = ( <MyComponent>{"Some content"}</MyComponent> );
const myBlock = createBlock( "example/my-block", {
content: renderToString( myContent )
} );
And here is my block’s save function:
( { className, attributes } ) => (
<RichText.Content
tagName="div"
className = { className }
value = { attributes.content }
/>
)
I tried this solution since I have other custom blocks that use RichText, and those blocks weren’t having an issue with unwanted escaping. But I don’t know why I needed to use RichText for the blocks that were having an issue. The content of the affected blocks is generated automatically and contains no formats, core or custom.
One last note. The “undefined” and “null” that appeared in my block’s wrapper on the front end when using <RawHTML>{ attributes.content }</RawHTML>
in my block’s save function resulted from a front end script, not anything related to the block editor. My block’s save function was actually generating nothing inside the block wrapper.